🚀 CSP Level 3 - 次世代のXSS対策
🛡️ CSP Level 3の新機能
nonce(Number used once)とstrict-dynamicの組み合わせで、より強固なセキュリティを実現します。
• nonce:毎回異なるランダムな値で、スクリプトの実行を制御
• strict-dynamic:信頼されたスクリプトから動的に生成されたスクリプトも自動的に信頼
🎲 nonce(ナンス)の仕組み
nonceは「Number used once」の略で、毎回異なる乱数です。
const crypto = require('crypto');
const nonce = crypto.randomBytes(16).toString('base64');
✅ nonceを持つ正当なスクリプト
<script nonce="r@nd0m$tr1ng">
console.log("nonceが一致したので実行されます");
// アプリケーションの正常な処理
const userData = { id: 123, name: "山田太郎" };
console.log("ユーザーデータ:", userData);
</script>
❌ nonceなし = 実行拒否
<script>
// 攻撃者が注入しようとするコード(nonceがない)
console.log("悪意のあるスクリプト実行");
document.cookie;
</script>
<script src="https://evil.com/malicious.js"></script>
⚡ strict-dynamicの実際の動作
nonceを持つスクリプトから動的に生成されたスクリプトも自動的に信頼されます!
<script nonce="r@nd0m$tr1ng">
console.log("[親] nonceありスクリプトが実行開始");
// 実際のアプリケーションでよくある動的スクリプト生成
// 例:Google Analytics、広告タグ、サードパーティウィジェットなど
const script = document.createElement('script');
script.textContent = `
console.log("[子] 動的に生成されたスクリプトが実行されました");
console.log("[子] nonceなしでもstrict-dynamicにより実行可能");
// さらに孫スクリプトを生成(ネストした動的生成)
const grandchildScript = document.createElement('script');
grandchildScript.textContent =
'console.log("[孫] ネストして生成されたスクリプトも実行可能");';
document.body.appendChild(grandchildScript);
`;
document.body.appendChild(script);
</script>
なぜこれが重要なのか?
多くのモダンなWebアプリケーションは、動的にスクリプトを生成します:
- Google Analytics などの分析ツール
- 広告タグやリターゲティングピクセル
- チャットウィジェットやカスタマーサポートツール
- A/Bテストツール
strict-dynamicがないと、これら全てにnonceを付与する必要があり、実装が困難でした。
🛡️ JSONPバイパスも防げる!
CSP Level 3では、ホワイトリストは完全に無視されます。
// 攻撃者が試みるJSONPバイパス(CSP Level 2なら成功する)
<script src="https://trusted-cdn.com/jsonp?callback=console.log('XSS');document.cookie;//"></script>
// CSP Level 3では、nonceがないため実行されない
CSP Level 2 vs Level 3 の比較
攻撃手法 |
Level 2 |
Level 3 |
JSONPバイパス |
❌ 成功 |
✅ ブロック |
AngularJSバイパス |
❌ 成功 |
✅ ブロック |
信頼されたCDNからの攻撃 |
❌ 成功 |
✅ ブロック |
🎯 なぜCSP Level 3が優れているのか
- 予測不可能:毎回異なるnonceで、攻撃者は推測できない
- シンプル:ドメインリストの管理が不要
- 実用的:strict-dynamicで既存のコードも動作
- 堅牢:JSONPやAngularJSバイパスも防げる