この記事はレコチョク Advent Calendar 2025 の7日目の記事となります。
はじめに
はじめまして。レコチョクでエンジニアをしている清水と申します。
Tayoriというバンドを推しておりまして、先日ライブへ参加してボーカルの綺麗な声に泣きました。
さて最近、業務の中でCookieとSession周りを総点検する機会がありました。
その過程で、XSS / CSRF / SameSite / Secure / HttpOnly といった
「知っているつもりだったけど実は曖昧だった部分」がいくつも見つかりました。
せっかくなので、この機会にCookieとSessionの基本から
最近のブラウザ仕様にもとづくセキュリティ上の注意点までまとめて整理してみます。
Cookieの基本
Cookieは、ブラウザに保存される小さなデータです。
Webの通信プロトコルであるHTTPはステートレスなため、状態を保持することができません。
そこでユーザーごとの状態を保持するためにサーバー側がSet-Cookieヘッダーを返し、ブラウザは指示に従ってデータを保管します。
そうして保持されたCookieをリクエスト時のデータとして付与することで、ステートフルな通信が実現されます。
Cookieは保存期間に応じて、以下のように区別されます
- セッションCookie:ブラウザが終了するまで有効 ※1
- 永続的Cookie:expires / max-age に指定された時間まで有効
※1: セッションCookieの削除条件はブラウザや設定によって違うので注意
Cookieで重要な属性 ( SameSite / Secure / HttpOnly )
Cookieには属性を付与する事ができます。
その中でセキュリティ観点から特に重要なのが以下の3つになります。
SameSite
CSRF対策として導入された属性で、指定できる値は3種類あります。
- Strict : Cookie発行元サイトからのリクエスト時のみ送られる
- Lax : ブラウザのデフォルト値。Cookie発行元サイトからのリクエストに加え、外部サイトからのトップレベルナビゲーション(GET等の安全なHTTPメソッドに限る)の際も送られる ※2
- None : POST通信やXHR/Fetchなど、どのような外部リクエストでも送られる。Secure属性が必須
外部サイトからのPOST通信やFetch通信にCookie付与すると、不正に決済などを実行される可能性がありますので原則としてLax以上を指定するようにしましょう。
※2: より詳細な説明はこちらを確認ください
Secure
HTTPS通信でのみCookieを送信します。
SSL/TLSによって暗号化されていない通信は盗聴の可能性があり、簡単にCookieを抜き出すことができます。そのためHTTP通信ではCookieを送らないようにしましょう。
HttpOnly
JavaScriptからCookieを参照できないようにする属性です。
XSSが発生してもCookieを盗まれにくくなるため、認証トークンをCookieで保持する場合は必須です。
Sessionの基本
Sessionは、サーバー側にユーザーごとの状態を保持する仕組みです。
ブラウザにはsessionIdだけがCookieとして保存され、実際のデータはサーバー側のストア( Redis / ファイル / DB )に保存されます。
Sessionはあくまで「サーバー側に状態を持つ」方法であり、Cookieに直接データを入れない点が特徴です。 サーバーはCookieに保存されたsessionIdをもとにデータを取り出し、保持している状態を利用することができます。
Sessionのデータは、どこに保存するかによってパフォーマンスや有効期限といった挙動が変わります。 小規模ではファイルやメモリ、中〜大規模ではRedisなどの共有ストアを利用するケースが一般的です。
Cookie / Sessionのよくある落とし穴
CookieとSessionはシンプルな仕組みに見えますが、実務では思い込みや仕様理解の差によって問題が起きやすい部分でもあります。ここでは特に注意したいポイントを簡潔にまとめます。
1. HttpOnlyを付けてもCookieの「送信」は防げない
HttpOnlyは「JavaScriptから読み取れなくなる」属性であり、Cookieがリクエストに自動で付与される動作自体は変わりません。
そのためXSSが発生した場合、JSでCookieを盗むことはできなくても、Cookieによる認証状態のまま決済などを実行することが可能です。
2. SameSite=LaxでもGETのページ遷移ではCookieが送られる
SameSiteはCSRFを防ぐのに有効な属性ですが、Laxの場合は「外部リンクからの遷移(GET)」ではCookieが送られます。
そのため、GETリクエストに副作用を持たせるとCSRFの余地が残ってしまいます。
GETは副作用なしが原則という基本ルールが改めて重要になります。
3. Sessionは“ブラウザ終了=削除”ではない
一般的にsessionIdはセッションCookieとして保持されます。セッションCookieはブラウザの終了時に削除されるとされていますが、実際の挙動は設定やブラウザによって異なります。
そのためブラウザを終了したとしても必ずしもSessionが破棄されるとは限りません。
またRedis等のストアにも有効期限があり、その値によってはブラウザ終了前にSessionが破棄される可能性があります。
4. CookieのPathやDomainを広げすぎると干渉する
CookieのPathを/にしたり、Domainを広く指定しすぎると、
意図しないアプリケーションや別パスの処理にもCookieが送られてしまいます。
CSRFやXSSにつながる脆弱性となりますので、Cookieは必要な範囲に限定して作用させることが重要です。
5. セッションストアの違いで挙動が変わる
ロードバランサー配下でメモリストアを利用している場合、
サーバーごとにSessionが別管理になり「Sessionが勝手に切れたように見える」ことがあります。
スケールが必要な環境では、Redisなどの共有ストアを使うことが前提になります。
まとめ
Cookie はクライアント側に保存される小さなデータであり、一方でSessionはサーバー側に状態を保持する仕組みです。
用途や性質が異なる2つの手法が互いに補完し合うことでWebアプリはステートフルな体験を実現しています。
- Cookie は SameSite / Secure / HttpOnly が特に重要
- Sessionの有効期限は「ブラウザ側 + サーバー側」の短い方で決まる
- sessionIdはあくまで「鍵」であって、実データはサーバーに置く
- GETに副作用を持たせない・Cookieの作用範囲を最小にするなど一般的な注意も重要
今回改めて見直す中で、自分自身「知っているつもりだった部分」がかなり多かったと感じました。 同じように整理したい方の参考になれば幸いです。
参考
明日の レコチョク Advent Calendar 2025 は8日目、【Android】Maestro + GitHub ActionsによるPR作成時の自動テストです。お楽しみに!
清水日向
24卒で株式会社レコチョクに入社。
フロントエンド・バックエンドエンジニアをしています。
三度の飯よりもEDMが好き。