説明
信頼できないドメインからスクリプト、スタイル、モジュールを読み込むと、サプライチェーン上のリスクが生じます。読み込まれたスクリプトはページのコンテキストで実行されます。ドメインの所有者変更、サーバー侵害、DNS改ざんによって、アクセス可能なCookieやトークンの窃取、DOM改ざん、キー入力の記録、フィッシングへの誘導を行うコードが配信されるおそれがあります。2024年にはpolyfill.ioから悪意あるスクリプトが配信されました。
想定される影響
- ページに許可された権限での悪意あるコードの実行
- アクセス可能なCookie、ローカルストレージ、OAuthトークンの窃取
- フォームの入力、決済情報、個人情報の漏えい
- 偽のログイン画面や決済画面を表示するDOM改ざん
- 該当スクリプトを読み込むページやユーザーへの被害の拡大
対処方法
- polyfill.ioなど、信頼できないドメインからの読み込みを削除してください。
- 必要なポリフィルやライブラリは検証済みの固定バージョンを使い、可能なら自前で配信してください。
- CDNが必要なら信頼できる配信元を選び、バージョンを固定してSRI(
integrityとcrossorigin)を使ってください。 import()、createElement、fetch後のevalなどの動的読み込みでは、許可ドメインをコードやビルド設定で固定し、実行時の変更を制限してください。- CSPの
script-srcを'self'と厳密な許可リストに限定し、必要に応じて'strict-dynamic'、nonce、'sha256-...'ハッシュを使ってください。 - 古いブラウザーへの対応要件を見直し、機能検出によって必要なポリフィルだけを読み込んでください。
- 外部リソースの変更と整合性を監視し、依存関係を定期的に確認してください。
例
変更前
javascript
// server.js(Node/Express)- 変更前
import express from 'express';
const app = express();
// 環境変数で変更できますが、既定値はpolyfill.ioです。
// 配信元が侵害されると、悪意あるスクリプトがユーザーへ届くおそれがあります。
app.get('/', (req, res) => {
const polyfillUrl = process.env.POLYFILL_URL || 'https://polyfill.io/v3/polyfill.min.js?features=default';
res.send(`<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>App</title>
<script src="${polyfillUrl}"></script>
</head>
<body>
<h1>Hello</h1>
</body>
</html>`);
});
app.listen(3000);
変更後
javascript
// server.js(Node/Express)- 変更後
import express from 'express';
const app = express();
// 1) 自前配信: ビルド時に検証済みの固定バージョンを/assetsへ配置します。
app.use('/assets', express.static('public/assets', { immutable: true, maxAge: '1y' }));
app.get('/', (req, res) => {
// 外部ドメインに依存せず、検証済みのファイルを固定パスで配信します。
// 外部CDNを使う場合はバージョンを固定し、integrityとcrossoriginを設定します。
res.setHeader('Content-Security-Policy', "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'");
res.send(`<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>App</title>
<script src="/assets/polyfills-4.8.0.min.js" defer></script>
</head>
<body>
<h1>Hello</h1>
</body>
</html>`);
});
app.listen(3000);
説明:
- 変更前: polyfill.ioからスクリプトを直接読み込みます。配信元が侵害されると、ページのコンテキストで悪意あるJavaScriptを実行されるおそれがあります。実行時にURLを変えられる場合は、その変更権限も管理する必要があります。
- 変更後: 検証済みのファイルを固定パスで自前配信し、CSPで同一オリジンのスクリプトを許可します。外部CDNを使う場合は、許可ドメインの固定に加え、バージョン固定とSRIで信頼する原本との一致を確認し、サプライチェーンのリスクを減らしてください。