Cookieとは ― セッションIDとの関係
Cookieとは、
Cookieとは、サーバがブラウザに預けて送り返させる値
Cookieは、Webサーバがブラウザに預け、次からのリクエストに付けて送り返させる名前と値の組です。HTTPはリクエストを1つずつ切り離して扱うので、サーバはCookieを手がかりに、同じ利用者からのリクエストかを見分けます。
ログインした状態が次のページでも続くのは、この仕組みによるものです。多くのサイトは、Cookieに番号だけを入れ、利用者の情報はサーバの側に置きます。この番号がセッションIDです。
Cookieが要る理由は、HTTPが前のリクエストを覚えないから
HTTPの仕様であるRFC 9110は、3.3節でHTTPを状態を持たないプロトコルと定めています。1つ1つのリクエストは、それだけで意味が分かるように作られています。前にどんなリクエストが来たかによって、意味が変わることはありません。
同じ節によると、同じ接続で届いた2つのリクエストでも、原則として同じブラウザからのものと決めつけてはいけません。このままでは、ログインのリクエストと次のページのリクエストが、サーバから見て別々の出来事になります。そこでサーバは、最初の応答で目印を渡します。ブラウザは、次から目印を付けて送ります。
Set-CookieヘッダとCookieヘッダのやり取り
サーバは、レスポンスのSet-Cookieヘッダで、Cookieの名前と値をブラウザに渡します。ブラウザは、受け取ったCookieを保存します。同じサイトへ次にリクエストを送るとき、ブラウザは値をCookieヘッダに入れて付けます。
HTTP/1.1 200 OK Set-Cookie: SID=31d4d96e407aad42; Path=/; Secure; HttpOnly # SID が名前、31d4d96e407aad42 が値 # 「;」の後ろの Path・Secure・HttpOnly が属性
GET /mypage HTTP/1.1 Host: example.com Cookie: SID=31d4d96e407aad42 # 送り返すのは名前と値だけ
上の例は、Cookieの仕様であるRFC 6265の3.1節にあるものです。保存場所と流れる向きを、図にすると次のとおりです。
Cookieヘッダに入るのは、名前と値だけです。RFC 6265の4.2.2節のとおり、PathやSecureなどの属性は送り返されません。属性は、ブラウザがいつどこへ送るかを決めるための指示です。
Cookieを消したいとき、サーバは期限を過去の日時にしたSet-Cookieを送ります。ブラウザは、期限の切れたCookieを送らなくなります。
セッションIDとの関係:中身はサーバ、ブラウザは番号だけ
利用者の名前や権限を、Cookieにそのまま入れることもできます。ただし、RFC 6265の8.4節は、サーバが番号だけをCookieに入れる使い方が一般的だと述べています。サーバは、その番号を鍵にして、自分の手元の表から利用者の情報を引きます。
冒頭で触れたセッションIDは、この番号のことです。以下では、番号と中身を対応させる表をセッション表と呼びます。ブラウザが持つのは番号だけなので、Cookieを見られても利用者の情報そのものは漏れません。RFC 6265は、この点を番号を使う利点に挙げています。
その代わり、番号を知った人は、その利用者になりすませます。この危険は、後のセッションハイジャックの節で説明します。持っているだけで使える点は、アプリに権限を渡すOAuthのアクセストークンも同じです。サーバを複数台並べると、表を持たないサーバにリクエストが届くことがあります。その場合は、ロードバランサで同じ利用者を同じサーバに送り続けます。
動かして確かめる:ログインから次のページを開くまで
利用者Aがログインし、次のページを開くまでを5段に分けました。段を選ぶと、流れるメッセージと、ブラウザとサーバが持つ値が変わります。通信はHTTPSとします。最後の段では、別の人が同じ番号を送ります。
POST /login HTTP/1.1 Host: example.com id=user-a&password=(入力した値)
HTTP/1.1 200 OK Set-Cookie: SID=31d4d96e407aad42; Path=/; Secure; HttpOnly
GET /mypage HTTP/1.1 Host: example.com Cookie: SID=31d4d96e407aad42
HTTP/1.1 200 OK Content-Type: text/html (利用者Aのマイページ)
GET /mypage HTTP/1.1 Host: example.com Cookie: SID=31d4d96e407aad42
3段目と5段目で、サーバに届くリクエストは同じ形です。違うのは送り主だけで、サーバにはそれが見えません。
Cookieの種類:セッションCookieと有効期限付きのCookie
期限を書くかどうかで、残る長さが変わる
Set-CookieにExpiresもMax-Ageも書かないと、Cookieはブラウザが決めるセッションの終わりまでしか残りません。このCookieをセッションCookieと呼びます。MDNの説明では、セッションの終わりはブラウザを終了したときです。ただし、次の起動時にタブを復元する機能を使うと、セッションCookieも一緒に戻ります。
ExpiresかMax-Ageを書いたCookieは、ブラウザを閉じても期限まで残ります。Expiresは期限の日時で、Max-Ageは残りの秒数で書きます。両方あるときは、Max-Ageが優先です。期限は上限で、ブラウザは容量やプライバシーの理由で先に消すことがあります。
ファーストパーティとサードパーティ
開いているページのサイトが自分で発行したCookieは、ファーストパーティCookieです。ページの中に広告など別のサーバのコンテンツがあると、ブラウザはそのサーバにもリクエストを送ります。そのサーバが発行したCookieが、サードパーティCookieです。
RFC 6265の7.1節は、同じ第三者のコンテンツを置いた複数のサイトをまたいで、利用者を追跡できる点を挙げています。同じ節のとおり、サードパーティCookieの扱いはブラウザによって大きく違います。
Cookieの属性と、書かないときの動き
Set-Cookieの「;」の後ろに並ぶのが、属性です。属性は、ブラウザがCookieをどこへ、いつまで、どの通信で送るかを決めます。書かなかった属性には、次のような既定の動きがあります。
| 属性 | 付けた | 書かない |
|---|---|---|
| Domain | そのドメインと、 | 発行した |
| Path | そのパスと、 | リクエストした |
| Expires Max-Age | その期限まで | ブラウザが |
| Secure | HTTPSなど | HTTPでも |
| HttpOnly | ページの | スクリプトから |
| SameSite | 別の | ブラウザに |
Domainは、書くと送り先が広がり、書かないほうが狭くなります。Pathを書かないときは、たとえば /shop/cart への応答で受け取ったCookieなら /shop になります。 /login のように「/」が1つだけのパスなら、/ になります。RFC 6265の4.1.2.4節は、Pathは同じホストの中でCookieを分離して守る手段にはならないとしています。
Secureは盗聴から値を守りますが、偽のCookieの差し込みは防げません。RFC 6265の4.1.2.5節は、経路上の攻撃者がHTTPの通信からSecure付きのCookieを上書きできるとしています。RFC 6265の改訂版の草案は、この上書きを止める決まりを足しました。それでも、HTTPの応答を偽れば、同じサイトあての別のCookieを差し込めると草案の8.6節は述べています。
HttpOnlyを付けたCookieも、リクエストには付いて送られます。読めなくなるのは、document.cookie などを使うスクリプトからだけです。
SameSiteは、RFC 6265の改訂版の草案で定める属性
SameSiteは、RFC 6265には無い属性です。RFC 6265を置き換える改訂版の草案が定めています。2026年10月28日の時点で、この草案はIETFの承認を終え、RFCとして発行されるのを待つ段階にあります。RFCの番号は、まだ付いていません。
草案によると、Strictは、同じサイトから始まったリクエストにだけCookieを送ります。Laxは、それに加えて、別のサイトのリンクから移ってきたときのように、ページ全体が切り替わる移動にも送ります。MDNによると、この移動はGETのような安全なメソッドに限られ、POSTは含みません。Noneは、別のサイトから始まったリクエストにも送ります。Noneを使うときは、Secureも付ける必要があります。
SameSiteを書かなかったときの扱いは、ブラウザによって違います。草案は、Laxと同じ扱いを既定にしています。MDNの互換性の表では、Chromeは80から、Edgeは86から、書かないCookieをLaxとして扱います。FirefoxとSafariは、既定ではLaxとして扱いません。
Chromeには例外もあります。作られてから2分以内のCookieなら、別のサイトからのページ全体のPOSTにも付けます。Chromiumの公式の説明は、この例外を一時的な措置としています。
SameSiteで止めていなければ、ブラウザは、別のサイトのフォームから送らされたリクエストにもCookieを付けます。この性質を悪用し、利用者が意図しない操作をサイトに送らせる攻撃が、クロスサイトリクエストフォージェリです。SameSiteは、この攻撃を防ぐ手段の1つです。ただし草案は、Laxだけでこの攻撃を完全には防げないとしています。攻撃の流れと、トークンなどほかの対策は、CSRFの記事にまとめました。
Cookieが盗まれると、セッションハイジャックが起きる
セッションIDを他人に知られると、その人は利用者になりすましてサイトを使えます。IPAの「安全なウェブサイトの作り方」は、この攻撃をセッションハイジャックと呼んでいます。上の図の5段目が、この状態です。
IPAは被害の例として、不正な送金、ウェブメールの盗み見、設定の書き換えを挙げています。RFC 6265の8.3節によると、Cookieの中身を暗号化しても、盗んだCookieを別のブラウザで使われることは防げません。
番号を知られる経路
- 推測:番号が時刻などから単純な規則で作られていると、他人に当てられます。
- 盗聴:HTTPのまま流れたCookieは、中間者攻撃の記事で説明したように経路上で読まれます。
- スクリプトによる読み取り:XSSの記事で説明したように、埋め込まれたスクリプトにCookieを読まれます。
- 固定化:攻撃者が用意した番号を利用者に使わせ、その番号のままログインさせます。
守り方
IPAの「安全なウェブサイトの作り方」は、セッションIDの扱いについて次の対策を挙げています。HttpOnlyは、同じ資料のXSSの節で、Cookieの漏えいに備える保険的な対策に挙げられています。
- セッションIDを、推測されにくい乱数で作る
- セッションIDを、URLの中に入れない。リンク先のサイトに、URLごと送られることがあるため
- HTTPSで使うCookieに、Secure属性を付ける
- ログインに成功したら新しいセッションを始め、古いセッションIDを無効にする
- Cookieの有効期限を、必要以上に長くしない
- HttpOnly属性を付け、スクリプトからCookieを読めなくする
練習問題(全6問)
正解:サーバが
サーバは
正解:サーバの
ブラウザの
正解:ブラウザが
どちらも
正解:www.example.com にだけ送る
Domainを
正解:ページの
HttpOnlyを
正解:ログインに
固定化では、
選んでから「採点する」を押すと、解説つきで答え合わせができます。
参考にした資料
- RFC 6265 HTTP State Management Mechanism(Set-CookieとCookieヘッダ、属性、セッションID)
- draft-ietf-httpbis-rfc6265bis(RFC 6265の改訂版の草案。SameSite)
- RFC 9110 HTTP Semantics(HTTPが状態を持たないこと)
- MDN Set-Cookie(属性の既定の動きと、ブラウザごとの対応)
- Chromium SameSite Updates(ChromeがSameSiteを書かないCookieをLaxとして扱うこと)
- IPA 安全なウェブサイトの作り方 1.4 セッション管理の不備(セッションハイジャックと対策)
関連記事
おすすめの本 PR
ネットワークを初めて学ぶ人向けの入門書です。
![『[改訂新版]3分間ネットワーク基礎講座』の表紙](https://m.media-amazon.com/images/P/4774143731.09.MZZZZZZZ.jpg)
