「Cookie を httpOnly にすれば JavaScript から読めないので安全」
半分正しく、半分間違い。httpOnly 属性は document.cookie 経由での読み取りを防ぐが、Cookie を読めなくても悪用できるという点が見落とされがち。
また、httpOnly は「Cookie 窃取による恒久的なセッションハイジャック(別端末からのログイン)」を防ぐ防具ではあるが、「今その瞬間、ユーザーのブラウザを操ること」は防げない。即死を避けるヘルメットであり、飛んでくる矢(XSS)そのものを防ぐ盾ではない。
ブラウザには「リクエスト送信時に、対象ドメインの Cookie を自動的に付与する」という仕様がある。credentials: "include" を指定した fetch API も例外ではない。
攻撃者はこの仕様を利用して、Cookie の中身を一切知らないまま、被害者のブラウザに正規リクエストを送信させることができる。
1. 被害者が target-site.com にログイン済み
→ セッション Cookie がブラウザに保存されている
2. 被害者が攻撃者の罠サイトを訪問する
3. 罠サイトの JavaScript が以下を実行する:
fetch("https://target-site.com/api/password/reset", {
method: "POST",
credentials: "include", // Cookie が自動送信される
body: JSON.stringify({ newPassword: "attacker123" })
});
4. ブラウザがセッション Cookie を自動で付与してリクエストを送信
5. サーバーは正規ユーザーからのリクエストと区別できず、
パスワードが変更される → アカウント乗っ取り
攻撃者は Cookie の値を読む必要がない。 ブラウザに「送らせる」だけで目的を達成できる。
CSRF は「別サイトから」リクエストを飛ばす攻撃だが、XSS でサイト自身にスクリプトを注入された場合は別の問題が起きる。
1. 被害者が target-site.com にログイン済み
2. サイトに XSS 脆弱性があり、攻撃者のスクリプトが注入されている
3. 注入されたスクリプトが以下を実行する:
fetch('/api/post_message', {
method: 'POST',
body: 'スパムURL'
// credentials: "include" すら不要
// 同一オリジン宛てなので Cookie は自動で付く
});
4. ブラウザが httpOnly Cookie を自動で付与してリクエストを送信
5. サーバーは正規ユーザーからの投稿と見なし、スパムが送信される
これを Session Riding(セッションへの相乗り) と呼ぶ。
クロスオリジンではないため、SameSite 属性も CORS の設定も関係なくCookie は送信される。CSRF 対策を万全にしていても、XSS 脆弱性があれば同様の被害が起きる。
| 脅威 | httpOnly で防げるか |
|---|---|
| XSS による Cookie の 読み取り・窃取 | ✅ 防げる |
| CSRF による Cookie の 自動送信を利用した操作 | ❌ 防げない |
| XSS + Session Riding による 同一オリジンへの不正操作 | ❌ 防げない |
httpOnly はあくまで XSS による Cookie 窃取 に対する緩和策であり、CSRF・Session Riding には無力。
CORS はブラウザが レスポンスの読み取り を制御する仕組みであり、リクエストの送信自体を常に止めるわけではない。
- (補足)Simple Request はクロスオリジンでも preflight なしに送信が通る点に注意。
Content-Type: text/plainなどの条件を満たす Simple Request は、preflight(OPTIONS リクエスト)なしで本リクエストが送信される。- サーバー側で状態変更(パスワードリセットなど)が実行された後に、ブラウザがレスポンスをブロックするという順序になり得る。
CORS の設定が甘い場合(Access-Control-Allow-Origin: * と Access-Control-Allow-Credentials: true の組み合わせなど)はさらに危険。
また、Session Riding(XSS 起因)は同一オリジンの問題なので CORS はそもそも無関係。
httpOnly 単体では不十分。以下を組み合わせること。
Session Riding の根本対策は XSS 脆弱性をなくすこと。 HTML 出力時のエスケープ徹底、Content Security Policy(CSP)の設定など、スクリプト注入を許さない実装が前提となる。CSRF 対策をどれだけ積み上げても、XSS があれば同一オリジンから攻撃が成立する。
リクエストごとにサーバーが発行するランダムなトークンをリクエストに含め、サーバー側で検証する。攻撃者はトークンを知ることができないため、不正なリクエストを弾ける。
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax
Strict― クロスサイトからのリクエストには一切 Cookie を付与しないLax― トップレベルナビゲーション(リンククリックなど)の GET のみ許可し、POST などには付与しない
Access-Control-Allow-Originにはワイルドカード (*) を使わず、信頼するオリジンを明示するAccess-Control-Allow-Credentials: trueを使う場合は特に厳密に管理する
パスワード変更などの高リスク操作では、現在のパスワードの再入力や多要素認証を要求することで、CSRF・Session Riding が成功しても被害を防げる。