web-dev-qa-db-ja.com

Electron AppでCSP HTTPヘッダーを定義する

APIドキュメント に従って、ElectronアプリケーションのレンダラーのContent-Security-Policy HTTPヘッダーを定義する方法がわかりません。 DevToolsで常に警告が表示されます。

私は試した:

1)API Docのコードをやみくもにコピー/貼り付けます:

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

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

(ちなみに、文字列に「Content-Security-Policy:」が欠落している理由はわかりませんが、追加しても何も変わりません)

2)同じコードでレンダラーのセッションを変更する:

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

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

3)レンダラーに追加のヘッダーを追加します。

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

...

唯一機能するのは、レンダラーHTMLファイルでメタタグを使用することです。

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

ドキュメントにこの壊れたコードが含まれている理由がわかりません。それは私から地獄を混乱させましたが、試行錯誤によって実用的な解決策を見つけました:

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

したがって、headers引数は、details.responseHeadersで受信した元のヘッダーと同じ構造を持つオブジェクトでなければなりません。このオブジェクトは元の応答ヘッダーを完全に置き換えるように見えるため、渡されたオブジェクトにも元のヘッダーを含める必要があります。

extraHeadersオプションは、応答ヘッダー用ではありません。サーバーに送信される要求ヘッダー用です。

8
kayahr

目的が開発モード(http://プロトコルでロードされたリソース)とprodモード(file://プロトコル)の両方でCSPを使用できるようにする場合、次のようにできます。

最初に、Content-Security-Policyメタデータをsrc/index.htmlから削除します。prodモードでのみ挿入する必要があります。

  • onHeadersReceivedfile://プロトコルではElectron docs confirm として機能しません。また、
  • 開発モードではsrc/index.htmlのままにしておくと、少なくともリソースの一部でonHeadersReceivedがオーバーライドされ、開発モードでは異なる設定が必要になります。

次に、 gulp-inject を使用して、Prodモード用にそれを注入できます。

// 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'));
});

次に、npx gulp insert-prod-headersがたとえばng builddist/index.htmlを生成します。

そして、開発モードでは、Electron docsと同様にonHeadersReceivedを使用します

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(...)
}

このソリューションは、Electron 4.0.3でテストされました。

1
Artem Vasiliev

最初の読み込みで問題が発生しているのか、それともその後のWeb要求で問題が発生しているのかを知るのに十分な詳細はありませんが、私の問題は初期ファイルの読み込みに関するものでした。 Reactを使用するElectronアプリでは、kayahrのコードでもインラインスクリプトを使用することについて警告が表示されていました。これは、onHeadersReceivedメソッドが、アプリケーションが最初にロードされた後に行われた要求のみをキャッチするためです。最初のアプリケーションロードからの警告は停止しません。

アプリケーションのビルド中にテンプレートを使用して、インラインスクリプトとスタイル、およびアプリケーションが最初に読み込むHTMLファイルのCSPヘッダーにナンスを追加する必要がありました。

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'nonce-<%= scriptNonce %>'; style-src 'nonce-<%= styleNonce %>';">
    <link rel="stylesheet" type="text/css" href="./index.css" nonce=<%= styleNonce %>>
    <title>Basic Electron App</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="application/javascript" nonce=<%= scriptNonce %>>
      require('./index.js');
    </script>
  </body>
</html>

index.css

body {
  margin: 0px;
}

.hello {
  font-family: "Century Gothic";
  width: 800px;
  margin: 70px auto;
  text-align: center;
}

そしてgulfile.jsで次のものを既存のものに追加し、このタスクがパイプラインに含まれていることを確認します。以下のコードで現在のhtmlタスクを更新することもできます。

const template = require('gulp-template');
const uuidv4 = require('uuid/v4');

gulp.task('copy-html', () => {
  // Create nonces during the build and pass them to the template for use with inline scripts and styles
  const nonceData = {
    scriptNonce: new Buffer(uuidv4()).toString('base64'),
    styleNonce: new Buffer(uuidv4()).toString('base64')
  };
  return gulp.src('src/*.html')
  .pipe(template(nonceData))
  .pipe(gulp.dest('dist/'));
});

これは非常に単純な例です。 https://github.com/NFabrizio/data-entry-electron-app にもっと完全な例があります私が使用しているパッケージは、react-beautiful-dndのプルです。これは、インラインスタイルを追加しますが、現在はnonceを受け入れません。

0
NFab

Electron docs で指摘されているように、renderer.htmlスキームを介してfile://を読み込む場合、htmlファイルでコンテンツセキュリティポリシー(CSP)メタタグを使用する必要があります(上記の例でIIRCを実行します)。

Prodおよびdev環境の条件に応じてコンテンツセキュリティポリシーを調整する場合は、ビルドステップでHTML内にこの文字列を動的に生成できます。 mustache.js(例で使用)のようなテンプレートエンジンを使用することをお勧めします。

例(ファイルリソース)

私の場合、websocketとfile://リソースを介して開発モードでホットモジュール交換(HMR)を有効にしたかったため、CSPルールを緩和する必要がありました(ただしdev!のみ)。

index.mustache:

<html>
  <head>
    <meta
      http-equiv="Content-Security-Policy"
      content="{{{cspContent}}}"
    />
  </head>
...

開発者向けのcspContent.json:

{
  "cspContent": "default-src 'self'; connect-src 'self' ws:"
}

devのビルドステップ(prodのデフォルト値を使用できます):

npx mustache cspContent.json index.mustache > index.html

URLリソース

URLリソースを使用する場合は、 この例 に固執できます。

const { session } = require('electron')

session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
  callback({
    responseHeaders: {
      ...details.responseHeaders,
      'Content-Security-Policy': ['default-src \'none\'']
    }
  })
})

カスタムCSP応答ヘッダーを必ずデフォルトのヘッダーとマージしてください。上記の貼り付けられた例ではそうしません。ここでは、環境を条件付きで確認することもできます。

それが役に立てば幸い。

0
ford04