web-dev-qa-db-fra.com

Définir l'en-tête HTTP CSP dans l'application Electron

Suite à la documentation de l'API , je ne comprends pas comment définir un en-tête HTTP Content-Security-Policy pour le rendu de mon application Electron. Je reçois toujours un avertissement dans les outils de développement.

J'ai essayé:

1) Copier/coller le code dans l'API Doc, à l'aveuglette:

app.on('ready', () => {
    const {session} = require('electron')
    session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
        callback({responseHeaders: `default-src 'self'`})
    })

    win = new BrowserWindow(...)
    win.loadUrl(...)
}

(Au fait, je ne comprends pas pourquoi "Content-Security-Policy:" est manquant dans la chaîne. Mais l'ajouter ne change rien.)

2) Modification de la session du moteur de rendu avec le même code:

win = new BrowserWindow(...)
win.loadUrl(...)

const ses = win.webContents.session;
ses.webRequest.onHeadersReceived((details, callback) => {
  callback({responseHeaders: `default-src 'self'`})
})

3) Ajoutez un en-tête supplémentaire au moteur de rendu:

win = new BrowserWindow(...)
win.loadURL(`file://${__dirname}/renderer.html`,{
    extraHeaders: `Content-Security-Policy: default-src 'self'`
});

...

La seule chose qui fonctionne consiste à utiliser une balise META dans le fichier HTML de rendu:

<meta http-equiv="Content-Security-Policy" content="default-src 'self'>
19
Anozer

Pas sûr pourquoi la documentation contient ce code cassé. Cela m'a dérouté mais j'ai trouvé une solution efficace par essais et erreurs:

session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
    callback({ responseHeaders: Object.assign({
        "Content-Security-Policy": [ "default-src 'self'" ]
    }, details.responseHeaders)});
});

Ainsi, l'argument d'en-tête doit être un objet ayant la même structure que les en-têtes d'origine reçus dans details.responseHeaders. Et les en-têtes d'origine doivent également être inclus dans l'objet transmis car cet objet semble remplacer complètement les en-têtes de réponse d'origine.

L'option extraHeaders ne s'applique pas aux en-têtes de réponse. Il s’agit des en-têtes de requête envoyés au serveur.

7
kayahr

Si votre objectif est de pouvoir utiliser CSP à la fois en mode dev (avec des ressources chargées par le protocole http://) et en mode prod (protocole file://), voici comment procéder:

Tout d’abord, supprimez la méta Content-Security-Policy de src/index.html - nous devons l’injecter uniquement pour le mode prod, car 

  • onHeadersReceived ne fonctionnera pas pour le protocole file:// car Electron docs confirm , et aussi parce que 
  • si nous le conservons en src/index.html pour le mode Dev, il remplacera onHeadersReceived au moins pour une partie des ressources, et pour le mode Dev, nous avons besoin de paramètres différents.

Ensuite, nous pouvons l’injecter en mode Prod avec gulp-inject :

// in project dir
npm install --save-dev gulp-inject gulp
// src/index.html

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <base href="">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- inject:prod-headers -->
  <!-- src/prod-headers.html content will be injected here -->
  <!-- endinject -->
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
  <app-root>Loading...</app-root>
</body>
</html>

// src/prod-headers.html
<meta http-equiv="Content-Security-Policy" content="default-src 'self'>
// gulpfile.js
var gulp = require('gulp');
var inject = require('gulp-inject');

gulp.task('insert-prod-headers', function () {
  return gulp.src('./dist/index.html')
    .pipe(inject(gulp.src('./src/prod-headers.html'), {
      starttag: '<!-- inject:prod-headers -->',
      transform: function (filePath, file) {
        // return file contents as string
        return file.contents.toString('utf8')
      }
    }))
    .pipe(gulp.dest('./dist'));
});

Ensuite, assurez-vous que npx gulp insert-prod-headers est exécuté après, par exemple. ng build génère dist/index.html.

Et pour le mode dev, utilisons onHeadersReceived de la même manière que Electron docs example :

const args = process.argv.slice(1);
const devMode = args.some((val) => val === '--serve');
app.on('ready', () => {
    if (devMode) {
      const {session} = require('electron')
      session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
        callback({responseHeaders: `default-src http: ws:`})
      })
    }

    win = new BrowserWindow(...)
    win.loadUrl(...)
}

Cette solution a été testée sur Electron 4.0.3.

0
Artem Vasiliev