2022/02/22 記事の構成を大きく変えました
Electron × Content-Security-Policy
ElectronはContent-Security-Policy(CSP)を有効にすることを推奨している。
Content Security Policy (CSP) は、クロスサイトスクリプティング攻撃やデータインジェクション攻撃から保護する副層です。 Electron 内でロードする任意のウェブサイトで有効にすることを推奨します。
※ リンク先は英語ページ
Material-UI v4
Material-UI v4はjssが使われているので、CSPへの対処方法もjssに従ったものになっている。
secure-electron-template のissue#14 のコメントの中には具体的な方法が書かれている(今までお世話になりました)。
The way that you do this is by passing a tag in the
of your HTML. JSS will then, by convention, look for a <meta property="csp-nonce" tag and use the content value as the nonce.
You must include this header regardless of whether or not SSR is used. Here is an example of what a fictional header could look like:
<head>
<meta property="csp-nonce" content="this-is-a-nonce-123" />
</head>
MUI v5
MUI v5からはjssに代わってemotionが使われている。そのため、jssのやり方である
<meta property="csp-nonce" content="this-is-a-nonce-123" />
は使えない。
MUIのCSPのページを見てみると下記のようになっていた。
const cache = createCache({
key: 'my-prefix-key',
nonce: nonce,
prepend: true,
});
function App(props) {
return (
<CacheProvider value={cache}>
<Home />
</CacheProvider>
);
}
createCacheに渡すnonce
と<meta http-equiv="Content-Security-Policy" .../>
の中に書いたnonce
の値を一致させればいい。
ということで、
const nonce = document.head.querySelector('[property~=csp-nonce][content]')?.getAttribute('content') as string;
const cache = createCache({
key: 'my-prefix-key',
prepend: true,
nonce,
});
自分でmetaタグからnonceを取ってくる。webpackやhtml(ejs)の設定は、secure-electron-template のissue#14 のコメントの中の方法から変わっていない。
別解(私はこっち派)
ElectronのCSPの指定方法は、metaタグだけではない。
メインプロセスで HTTP headers に書き込むことができる。
であれば、
- メインプロセスでnonceを作り、HTTPヘッダに書き込む
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': [
"base-uri 'self'",
"object-src 'none'",
"script-src 'self'",
"frame-src: 'none'",
"worker-src 'none'",
`style-src 'self' 'nonce-${nonce}'`,
],
},
});
});
- レンダラープロセスから取得できるように公開する
ipcMain.handle('nonce', () => nonce);
const api = {
nonce: () => ipcRenderer.invoke('nonce')
};
contextBridge.exposeInMainWorld('api', api);
- レンダラープロセスでnonceをCacheProviderに渡す
(async () => {
const nonce = await window.api.nonce();
const cache = createCache({
key: 'my-prefix-key',
prepend: true,
nonce,
});
ReactDOM.render(
<CacheProvider value={cache}>
...
</CacheProvider>
, document.getElementById('root'));
})();
ejsもwebpackも使う必要もないし、nonceがHTMLにべた書きになることもない。