【入門編】カスタムHTTPヘッダー(X-Requested-With等)によるCSRF防御の限界 – アプリケーションセキュリティ & 安全な開発防御ガイド

「カスタムヘッダーさえあれば大丈夫」は罠?CSRF防御の盲点をエンジニア視点で紐解く

こんにちは。現場で泥臭いインシデント対応をしていると、「これさえやっておけば絶対安全」という言葉ほど怖いものはないと痛感します。

今回は、Web開発の現場でよく「CSRF(クロスサイト・リクエスト・フォージェリ)対策として有効だよね?」と誤解されがちな「カスタムHTTPヘッダー」についてお話しします。

「X-Requested-With」のようなヘッダーを付けておけば、悪意あるサイトからの攻撃を防げる……そう信じているなら、少し立ち止まって一緒に考えてみましょう。

—

1. まずは「CSRF」を身近な例でイメージしよう

CSRF(クロスサイト・リクエスト・フォージェリ)は、「泥棒があなたのフリをして、あなたの家の鍵(セッション情報)を勝手に使う」ような攻撃です。

1. あなたが銀行のサイトにログインしているとします。
2. その状態で、泥棒が作った罠サイト(悪意あるページ)を誤って開いてしまいます。
3. 罠サイトがブラウザに「銀行の送金ボタンを裏で押せ!」と命令します。
4. ブラウザは「あ、これ本人からの操作だ!」と勘違いして、あなたの銀行口座から泥棒の口座へ送金してしまいます。

これがCSRFの恐ろしさです。ブラウザは「ログイン状態」という鍵を自動的に付けてリクエストを送ってしまうため、泥棒がその鍵を盗まなくても、あなた自身の手を使って操作できてしまうのです。

—

2. 「カスタムヘッダー」が防御策とされる理由

そこで登場するのが「カスタムヘッダー」です。
例えば、JavaScriptからAjaxでリクエストを送る際、X-Requested-With: XMLHttpRequest のようなヘッダーを付けます。

「泥棒(悪意あるサイト)は、他のサイトに対して自由にカスタムヘッダーを付けられないはずだ。だから、このヘッダーがあれば正当なリクエストだと判断できる!」

……というのが、この対策の理屈です。確かに、昔のブラウザの仕様(Same-Origin Policy)では、他ドメインへのリクエストにカスタムヘッダーを付けることは困難でした。しかし、今はそう簡単ではありません。

—

3. なぜ「カスタムヘッダーだけ」では不十分なのか?

ここが今日の核心です。現代のWebではCORS(Cross-Origin Resource Sharing)という仕組みがあり、サーバー側の設定次第で、他ドメインからのリクエストも許可できるようになっています。

攻撃のメカニズム:プリフライトリクエストの罠

もし、Web APIの設定でCORSを少し緩く設定していたらどうなるでしょうか?

1. 泥棒の罠サイトは、ブラウザに「カスタムヘッダー付きで、あのAPIを叩け!」と指示します。
2. ブラウザは「いきなり送っていいかな?」と確認のため、サーバーに「プリフライトリクエスト(OPTIONSメソッド)」を投げます。
3. サーバーが「お、このドメインからのヘッダー付きリクエストなら許可してやるよ」というレスポンス(Access-Control-Allow-Headers など)を返すと、ブラウザは喜んで攻撃者のリクエストを実行してしまいます。

つまり、「カスタムヘッダーは、CORSの設定ミスや脆弱性と組み合わさると、いとも簡単に突破される」のです。泥棒が窓ガラスを割るのではなく、「合鍵屋」に偽の許可証を出させて鍵を作らせるようなものですね。

—

4. 私たちが本当にやるべきこと

では、どうすればいいのでしょうか?答えはシンプルで、「多層防御」です。カスタムヘッダーはあくまで「補助」と考えましょう。

実践すべき対策の具体例

CSRF対策の王道は「トークン」です。サーバー側で発行した「使い捨ての秘密の番号」を、リクエストに含めてチェックする方法です。

サーバー側の擬似的なチェックコード(Node.js / Expressの例)

// リクエストのヘッダーやボディからトークンを確認する
function verifyCsrfToken(req, res, next) {
const userToken = req.headers[‘x-csrf-token’]; // カスタムヘッダーでトークンを受け取る
const serverToken = req.session.csrfToken; // サーバーが発行したもの

if (!userToken || userToken !== serverToken) {
return res.status(403).send(‘不正なリクエストです!’);
}
next();
}

さらに安全を高めるために

1. SameSite属性の活用: Cookieに SameSite=Strict または Lax を設定しましょう。これだけで、他サイトからのリクエスト時にCookieが送信されなくなるため、CSRFの多くは無効化されます。
2. CORS設定の厳格化: 「とりあえず Access-Control-Allow-Origin: にしておけ」は絶対にNGです。許可するドメインは最小限に絞りましょう。

—

最後に:一歩ずつ学んでいきましょう

「カスタムヘッダーだけで守れる」という甘い考えを捨て、「ブラウザの機能は進化しているからこそ、攻撃も巧妙になっている」という視点を持つことが、信頼されるエンジニアへの第一歩です。

まずは、自分の開発しているアプリケーションのCookie設定やCORSの設定を見直してみてください。「なぜこの設定なのか?」を説明できるようになったとき、あなたのセキュリティ意識は格段にレベルアップしていますよ。

もし何か分からないことがあれば、いつでも聞いてください。一緒に泥臭く、安全なコードを書いていきましょう!

コメント

タイトルとURLをコピーしました