LoginSignup
6
2

More than 1 year has passed since last update.

MUI v5をElectronで使うメモ

Last updated at Posted at 2021-09-18

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ヘッダに書き込む
main.ts
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}'`,
            ],
        },
    });
});
  • レンダラープロセスから取得できるように公開する
main.ts
ipcMain.handle('nonce', () => nonce);
preload.ts
const api = {
    nonce: () => ipcRenderer.invoke('nonce')
};
contextBridge.exposeInMainWorld('api', api);
  • レンダラープロセスでnonceをCacheProviderに渡す
index.tsx
(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にべた書きになることもない。

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2