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'>
ドキュメントにこの壊れたコードが含まれている理由がわかりません。それは私から地獄を混乱させましたが、試行錯誤によって実用的な解決策を見つけました:
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({ responseHeaders: Object.assign({
"Content-Security-Policy": [ "default-src 'self'" ]
}, details.responseHeaders)});
});
したがって、headers引数は、details.responseHeaders
で受信した元のヘッダーと同じ構造を持つオブジェクトでなければなりません。このオブジェクトは元の応答ヘッダーを完全に置き換えるように見えるため、渡されたオブジェクトにも元のヘッダーを含める必要があります。
extraHeaders
オプションは、応答ヘッダー用ではありません。サーバーに送信される要求ヘッダー用です。
目的が開発モード(http://
プロトコルでロードされたリソース)とprodモード(file://
プロトコル)の両方でCSPを使用できるようにする場合、次のようにできます。
最初に、Content-Security-Policy
メタデータをsrc/index.html
から削除します。prodモードでのみ挿入する必要があります。
onHeadersReceived
はfile://
プロトコルでは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 build
はdist/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でテストされました。
最初の読み込みで問題が発生しているのか、それともその後の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を受け入れません。
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リソースを使用する場合は、 この例 に固執できます。
const { session } = require('electron')
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': ['default-src \'none\'']
}
})
})
カスタムCSP応答ヘッダーを必ずデフォルトのヘッダーとマージしてください。上記の貼り付けられた例ではそうしません。ここでは、環境を条件付きで確認することもできます。
それが役に立てば幸い。