Cookieでセッション管理するときに知っておきたいこと
CookieにアクセスできるJSのコードの条件
JavaScript から document.cookie
を使ってクッキーにアクセス(読み書き)するためには、主に以下の条件を満たす必要がある
-
HttpOnly フラグが付与されていないこと
-
HttpOnly
が付いているクッキーは、JavaScript 側からは一切読めない。 - 例:
Set-Cookie: session=…; HttpOnly
→ JS からは不可
-
-
ドメイン/パスの一致
- クッキーの
Domain
属性に指定されたドメイン(例:.example.com
)か、そのサブドメイン(例:app.example.com
)でなければアクセスできない。 - クッキーの
Path
属性(例:/foo/
)が現在のページのパス(例:/foo/bar.html
)のプレフィックスになっている必要がある。
- クッキーの
-
セキュア環境(Secure フラグ)
-
Secure
指定のあるクッキーは、HTTPS(https://
)でロードされたページでのみ読み書き可能。 - HTTP(
http://
)では見えない。
-
-
SameSite ポリシー
-
SameSite=Strict
/Lax
の場合、クロスサイトの文脈(iframe 埋め込みや外部リンククリック)で送信・参照が制限される。 -
SameSite=None; Secure
を設定していないと、第三者コンテキストではそもそもクッキーがブラウザに送られず、JS からも見えない。
-
-
同一オリジン(Same‑Origin Policy)
- プロトコル(http/https)、ホスト、ポートの組み合わせが完全に同一である必要がある。
- 例:
https://example.com:443
上のスクリプトは、同じオリジンのクッキーしか操作できない。
-
ブラウザのプライバシー設定
- サードパーティークッキー全般をブロックしている場合、iframe 内などの第三者コンテキストではクッキー自体が利用できず、JS からの参照も不可になる。
例:JavaScript での読み書きコード
// 書き込み(有効期限7日、Secure/SameSite=Lax)
document.cookie = "user=alice; Path=/; Max-Age=" + (7*24*3600) + "; Secure; SameSite=Lax";
// 読み込み
console.log(document.cookie); // HttpOnly 以外の、自ドメインかつパス一致のクッキー一覧が文字列で返る
―以上が、JS でクッキーにアクセスする際の主要な条件。
Cookieでセッション管理しているサービスで、セッションを盗まれるパターン
セッションを盗まれる(セッションハイジャックされる)代表的なケースと、その要因は大きく次のとおり。
1. ネットワーク上の盗聴(サイドジャッキング/MITM)
- HTTP 送信:Secure 属性のないクッキーを平文の HTTP 経路で送ると、パケットキャプチャ(Wi‑Fi の盗難アクセスポイントなど)で抜き取られる。
-
対策:常に HTTPS を使い、クッキーに
Secure; HttpOnly
を付ける。
2. クロスサイトスクリプティング(XSS)
-
スクリプト実行:サイトに脆弱性があり、攻撃者が悪意のある JavaScript を注入すると、
document.cookie
でクッキーを読み取り外部送信できる。 -
対策:入力値の厳格なサニタイズ/エスケープ、Content Security Policy(CSP)の導入、クッキーに
HttpOnly
属性を必ず付与。
3. セッション固定攻撃(Session Fixation)
- 初期セッションの押し付け:攻撃者が予め用意したセッション ID(固定されたクッキー)を犠牲者に使わせ、認証後もそのまま同じ ID を使わせる。
- 対策:ログイン時に必ずセッション ID を再生成し(セッションリジェネレーション)、既知のセッション ID は無効化。
4. クロスサイトリクエストフォージェリ(CSRF) ※直接盗むわけではないが…
- 権限の横取り:被害者がログイン済み状態で、別サイトから不正なリクエストを飛ばされることで、被害者のセッション権限を悪用される。
- 対策:トークン(CSRF トークン)や SameSite 属性で外部サイトからの不正送信を防止。
5. 物理/クライアントマシン上のマルウェア
- マルウェア感染:キーロガーやブラウザプラグイン型マルウェアが、ローカルに保存されたクッキーを抜き取る。
- 対策:端末のウイルス対策、不要プラグインの排除、短めの有効期限設定。
6. ログ/バックアップからの露出
- サーバー側ログ:デバッグやアクセスログに誤ってクッキー(セッショントークン)を記録し、そのバックアップが流出。
- 対策:ログに機密情報を残さない、トークンはマスクやハッシュ化して記録。
まとめとベストプラクティス
- HTTPS + Secure 属性
- HttpOnly 属性 で JS アクセスを制限
- SameSite=strict or Lax でクロスサイト侵害を抑止
- SSO・多要素認証 の併用
- セッション固定対策(ログイン時の ID 再生成)
- 入力のサニタイズ/CSP で XSS を防ぐ
CSRFの具体的な流れ
以下は、典型的な CSRF(Cross-Site Request Forgery)の攻撃がどう行われるかをステップごとの示したもの。
-
被害者が正規サイトにログイン
- ブラウザで
https://bank.example.com
にアクセスし、ID/PW を入力。 - サーバーは応答ヘッダで
Set-Cookie: session=ABC123; Secure; HttpOnly; SameSite=Lax
を返し、以降のリクエストに自動で添付されるようになる。
- ブラウザで
-
被害者はログイン状態のまま別サイトへ移動
- 例:
https://evil.attacker.com
を開く(あるいは広告経由でロードされる iframe)。 - この時点でブラウザにはまだ
bank.example.com
のセッション Cookie が保持されているが、正規フォームを送信してはいない。
- 例:
-
攻撃者側が不正リクエストを準備
- HTML の
<img>
タグや<form>
の自動送信スクリプトで、被害者の銀行口座から第三者へ送金するリクエストを仕込む。
<!-- GET リクエストの場合の例 --> <img src="https://bank.example.com/transfer?to=attacker&amount=1000" /> <!-- POST 自動送信フォームの場合の例 --> <form id="csrf" action="https://bank.example.com/transfer" method="POST"> <input type="hidden" name="to" value="attacker"> <input type="hidden" name="amount" value="1000"> </form> <script>document.getElementById('csrf').submit();</script>
- HTML の
-
ブラウザが自動的に Cookie を付与してリクエスト送信
-
<img>
や<form>
がロード/submit されると、同一オリジンポリシーによりbank.example.com
の Cookie(session=ABC123
)が自動でヘッダに乗る。 - 被害者は何も操作していないのに、ログイン中の銀行サイトへ不正リクエストが飛ぶ。
-
-
サーバー側が正当なリクエストと判断して処理
- サーバーは「認証済みユーザーからのリクエスト」と見なし、攻撃者指定の口座へ 1,000円振り込む処理を実行。
-
被害に気づきにくい
- 振込完了後も被害者画面には何も表示されず、バランス照会ページを見るまで異変に気づかない。
補足
悪意あるサイト(evil.attacker.com
)がユーザーのセッション Cookie を直接「読み取る」わけではない。ブラウザの仕組みを利用して、あたかも正規サイトに「ユーザー自身」がアクセスしたように振る舞わせ、ブラウザが自動的に Cookie を付与して送信してしまう、というのが CSRF の本質。以下ポイント。
-
Same-Origin Policy(SOP)で読み取りは不可
-
evil.attacker.com
上の JavaScript がdocument.cookie
でbank.example.com
の Cookie を参照したり、値を盗み出したりすることはできません。 - SOP により、スクリプトは自分と同じオリジン(プロトコル+ホスト+ポート)の Cookie にしかアクセスできないからです。
-
-
ブラウザが自動で Cookie を付与する仕組みを悪用
-
Cookie は、その
Domain
/Path
条件に合致するリクエストを行うと、ブラウザが必ずヘッダに載せて送信します(たとえクロスサイトの埋め込みリクエストでも)。 -
たとえば攻撃者ページに以下のような HTML を置くと――
<!-- GET リクエスト例:画像読み込み --> <img src="https://bank.example.com/transfer?to=attacker&amount=1000" />
――ブラウザは自動的に
Cookie: session=ABC123; …
をヘッダに付けてbank.example.com
にリクエストを投げます。
-
-
重要:攻撃者はレスポンスも Cookie の中身も見えない
-
<img>
タグでリクエストを仕込んでも、攻撃者側の JavaScript からはレスポンスの中身も、Cookie の値も一切読めません。 - しかしサーバー側(銀行)は「あ、このセッションは認証済みユーザーからのリクエストだ」と判断して振込処理を行ってしまいます。
-
-
POST フォームを自動 submit しても同様
<form id="csrf" action="https://bank.example.com/transfer" method="POST"> <input type="hidden" name="to" value="attacker"> <input type="hidden" name="amount" value="1000"> </form> <script> document.getElementById('csrf').submit(); </script>
-- ブラウザはフォーム送信時にも該当ドメインの Cookie を添付します。
補足まとめ
- 攻撃側は「ブラウザに正規サイト向けリクエストをさせる」だけ。
- ブラウザが持つ Cookie の自動送信機能(
Domain
/Path
マッチ)を利用し、ユーザーの意図しないリクエストを行わせる。 - 攻撃者側が Cookie の値を読むことはできませんが、サーバーはあくまで「有効なセッション付きリクエスト」として処理してしまうため被害が起こります。
防御策サマリ
-
CSRF トークン
- フォームにランダムなトークンを埋め込み、サーバー側で照合
-
SameSite=strict または Lax
- クロスサイトからの Cookie 送信を制限
-
Double Submit Cookie
- Cookie とリクエストボディ両方に同一のトークンを載せて検証
-
Referer/Origin ヘッダ検査
- リクエスト元が自ドメインかをサーバー側でチェック