「Cookieの設定くらい、適当でいいか」と思っている君へ。セッションハイジャックとCSRFの防波堤を盤石にする技術
現場で「なぜそのCookie属性が必要なのか?」と聞かれたとき、教科書の丸暗記ではなく、攻撃者の思考回路から説明できるだろうか。
今日扱うのは、Webセキュリティの基本中の基本、Cookieの `Secure`, `HttpOnly`, `SameSite` 属性だ。これらは単なる「設定項目」ではない。君が書いたアプリケーションの脆弱性を、インフラ側で最後の最後で食い止める「防波堤」だ。ここが甘いと、どんなに堅牢な認証ロジックを組んでも、ユーザーのセッションは簡単に奪われる。
今回は、なぜこの3つが必須なのか、そして実務でどう実装すべきかを、泥臭い知見を交えて解説する。
—
1. なぜ「設定するだけ」では足りないのか?
攻撃者が狙うのは、君のアプリが「無防備に渡すセッションID」だ。
- Secure属性がない場合: 攻撃者は公衆Wi-Fiなどでパケットを傍受(Sniffing)し、暗号化されていない通信経路からセッションIDを盗み出す。
- HttpOnly属性がない場合: XSS(クロスサイトスクリプティング)を仕込んだ攻撃者は、`document.cookie` を実行してセッションIDを窃取する。
- SameSite属性がない場合: CSRF(クロスサイトリクエストフォージェリ)により、ユーザーが意図しないうちに別のタブでログイン中のサイトへリクエストが送られ、送金や設定変更が実行される。
特に恐ろしいのは、これらが組み合わさった時だ。「HttpOnlyをつけてもXSSで攻撃される」「SameSiteをつけても完全ではない」という現実を理解し、多層防御を敷くのがプロの仕事だ。
—
2. 実装のベストプラクティス:コードで示す「守り」
現代のWeb開発において、Cookieは以下の設定がデフォルトであるべきだ。
PHPでのセッション設定(`php.ini`)
PHPの場合、`session_start()` を呼ぶ前にphp.iniやコード内で強制的に設定を注入する。
0, // ブラウザを閉じたら削除
‘path’ => ‘/’, // 全パスで有効
‘domain’ => ‘yourdomain.com’, // サブドメインを含まない場合は指定しないのが吉
‘secure’ => true, // HTTPSのみで送信
‘httponly’ => true, // JavaScriptからのアクセスを禁止
‘samesite’ => ‘Lax’ // CSRF対策の第一防衛線
]);
session_start();
Python (FastAPI/Starlette) での実装例
モダンなフレームワークでは、レスポンスオブジェクトに明示的に属性を付与する。
from fastapi import FastAPI, Response
app = FastAPI()
@app.post(“/login”)
def login(response: Response):
# セッションIDをセットする際の鉄則
response.set_cookie(
key=”session_id”,
value=”secret_token_12345″,
httponly=True, # JSからのアクセス遮断
secure=True, # HTTPS通信のみ
samesite=”lax”, # クロスサイトリクエスト制限
max_age=3600
)
return {“message”: “Logged in”}
—
3. インフラ側で「強制」する(Nginxによる二重防御)
コードレベルの実装漏れをカバーするために、リバースプロキシ(Nginx)でヘッダーを上書き・追加する設定は非常に有効だ。開発者がうっかり設定を忘れても、ここで止める。
Nginxの設定ファイル例
server {
# … 他の設定 …
# すべてのSet-Cookieヘッダーに対して属性を強制的に追加・置換する
# ※既に属性がある場合にも有効なよう慎重に設定すること
proxy_cookie_path / “/; HTTPOnly; Secure; SameSite=Lax”;
}
—
4. 現場で生き残るための「思考の盲点」
最後に、一つだけ覚えておいてほしい。`SameSite=Lax` は万能ではない。
例えば、`Lax` であればトップレベルのナビゲーション(リンククリック)ではCookieが送信されるため、巧妙に細工されたリンクを踏ませることで、GETリクエストによるCSRFが成功するケースがある。
- 機密性の高い操作(送金、パスワード変更)には、SameSiteに頼るな。
- 必ず Anti-CSRFトークン を実装し、リクエストの正当性を証明すること。
- Cookie属性は「最後の砦」であり、アプリケーションのロジックが脆弱であってはいけない。
まとめ:チェックリスト
- [ ] すべてのCookieに `Secure` がついているか?(ローカル開発環境を除き必須)
- [ ] JavaScriptから読み取る必要のないセッションIDには `HttpOnly` がついているか?
- [ ] `SameSite` は、用途に応じて `Lax` (デフォルト) または `Strict` に設定されているか?
- [ ] 開発ミスを防ぐため、WAFやロードバランサーで強制的にCookie属性を付与していないか?
セキュリティは、一度設定して終わりではない。技術の進歩とともに攻撃手法も進化する。だが、今日教えたこの「防波堤」を敷いておくだけで、君のシステムは圧倒的に攻撃しにくい標的になる。
さあ、コードを開いて、自分のアプリのCookie設定を確認してくれ。もし `Secure` が `false` になっていたら……今すぐ修正だ。

コメント