信頼できないソースからのWeb機能の読み込み

信頼できないソースからのWeb機能の読み込み

説明

信頼できないドメインからスクリプト、スタイル、モジュールを読み込むと、サプライチェーン上のリスクが生じます。読み込まれたスクリプトはページのコンテキストで実行されます。ドメインの所有者変更、サーバー侵害、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で信頼する原本との一致を確認し、サプライチェーンのリスクを減らしてください。

参考資料