1. はじめに
このセクションは規定ではありません。
本標準は、HTMLドキュメントおよびService Workerで実行されるスクリプト向けの非同期クッキーAPIを定義します。
HTTPクッキーは、Netscapeで誕生して以来(archive.orgに保存されたドキュメント)、ウェブのための有用な状態管理機構を提供してきました。
クッキーへの同期的・単一スレッド・スクリプトレベルのdocument.cookie
インターフェイスは、複雑さやパフォーマンス問題の原因となり、多くのブラウザが以下の点から移行したことでさらに悪化しました:
-
単一のブラウザプロセス、
-
単一スレッドのイベントループモデル、
-
クッキー操作中のスクリプトイベント処理が応答性を持つことへの一般的な期待がないこと
…から、現代のウェブは滑らかで高パフォーマンスな応答性を目指しています:
-
複数のブラウザプロセス、
-
マルチスレッド・複数イベントループモデル、
-
人間の反射速度スケールでの応答性への期待
現代のウェブでは、ウェブアプリケーションの一部でのクッキー操作が以下をブロックできません:
-
ウェブアプリケーションの他の部分、
-
ウェブオリジンの他の部分、
-
ブラウザ全体
Service Workerで構築された新しいウェブの部分もクッキーにアクセスする必要がありますが、同期・ブロッキングなdocument.cookie
インターフェイスは利用できません。なぜなら、ドキュメントを持たず、イベントループをブロックしては他のイベント処理に支障があるためです。
1.1. document.cookie
の代替
現在、クッキーを書き込む際はイベントループをブロックしながら、ブラウザが同期的にSet-Cookie
形式のクッキー文字列でクッキージャーを更新するのを待つ必要があります:
document. cookie= '__Secure-COOKIENAME=cookie-value' + '; Path=/' + '; expires=Fri, 12 Aug 2016 23:05:17 GMT' + '; Secure' + '; Domain=example.org' ; // これで書き込みが成功したと仮定できるが、失敗はサイレントなので判別しにくいため、 // 読み込みで書き込みが成功したか確認する必要がある var successRegExp= /(^|; ?)__Secure-COOKIENAME=cookie-value(;|$)/ ; if ( String( document. cookie). match( successRegExp)) { console. log( '成功しました!' ); } else { console. error( '成功しませんでした。理由は不明です' ); }
代わりに、以下のように書けるとしたらどうでしょうか:
const one_day_ms= 24 * 60 * 60 * 1000 ; cookieStore. set( { name: '__Secure-COOKIENAME' , value: 'cookie-value' , expires: Date. now() + one_day_ms, domain: 'example.org' }). then( function () { console. log( '成功しました!' ); }, function ( reason) { console. error( '成功しませんでした。理由はこちら:' , reason); }); // 書き込みを待つ間、他の処理もできる…
この方法には、ドキュメントに依存せずブロッキングもしないという利点があり、これによってスクリプトからクッキーにアクセスできないService Workerでも利用可能です。
また、setTimeout
によるポーリング型のクッキー監視を、クッキー変更のオブザーバーAPIで置き換える省電力な監視も含まれます。
1.2. 概要
要約すると、本APIは以下の機能を提供します:
-
クッキーの書き込み(または「設定」)と削除(または「有効期限切れ」)
-
読み取り(または「取得」)スクリプトから見えるクッキー
-
…Service Workerのコンテキストで指定されたリクエストパスにも対応
-
-
監視 スクリプトから見えるクッキーの変更を
CookieChangeEvent
で検知-
…長時間実行されるスクリプトコンテキスト(例:
document
) -
…Service Workerコンテキストでスクリプトが指定したリクエストパスにも対応
-
1.3. クッキーの検索
ドキュメントもService
Workerも、同じクエリAPIにアクセスできます。これはグローバルオブジェクトのcookieStore
プロパティ経由です。
get()
とgetAll()
メソッドでクッキーを検索します。
どちらもPromise
を返します。
両メソッドは同じ引数を取ることができ、引数は下記のいずれかです:
-
名前、または
-
オプション辞書(
getAll()
では省略可能)
get()
は、実質的にはgetAll()
の最初の結果のみを返す形です。
try { const cookie= await cookieStore. get( 'session_id' ); if ( cookie) { console. log( `「 ${ cookie. name} 」クッキーが見つかりました: ${ cookie. value} ` ); } else { console. log( 'クッキーが見つかりません' ); } } catch ( e) { console. error( `クッキーストアエラー: ${ e} ` ); }
try { const cookies= await cookieStore. getAll( 'session_id' }); for ( const cookieof cookies) console. log( `結果: ${ cookie. name} = ${ cookie. value} ` ); } catch ( e) { console. error( `クッキーストアエラー: ${ e} ` ); }
Service Workerは、fetchで 自身のスコープ下の任意URLに送信されるクッキー一覧を取得できます。
ドキュメントは、自身の現在URLのクッキーのみ取得できます。つまり、Documentコンテキストで有効なurl
値はドキュメントのURLのみです。
get()
やgetAll()
で返されるオブジェクトには、クッキーストア内の関連情報がすべて含まれています。古いdocument.cookie
APIのようにnameやvalueだけではありません。
await cookie= cookieStore. get( 'session_id' ); console. log( `クッキーのスコープ - Domain: ${ cookie. domain} Path: ${ cookie. path} ` ); if ( cookie. expires=== null ) { console. log( 'クッキーはセッション終了時に期限切れ' ); } else { console. log( `クッキーの有効期限: ${ cookie. expires} ` ); } if ( cookie. secure) console. log( 'このクッキーはセキュアオリジンに限定されています' );
1.4. クッキーの変更
ドキュメントもService
Workerも、同じ変更APIにアクセスできます。これはグローバルオブジェクトのcookieStore
プロパティ経由です。
クッキーの作成や変更(書き込み)はset()
メソッドで行います。
try { await cookieStore. set( 'opted_out' , '1' ); } catch ( e) { console. error( `クッキーの設定に失敗: ${ e} ` ); }
上記のset()
呼び出しは、オプション辞書を使う場合の省略形です:
await cookieStore. set({ name: 'opted_out' , value: '1' , expires: null , // セッションクッキー // デフォルトではdomainはnull(現在のドメインに限定) domain: null , path: '/' });
クッキーの削除(有効期限切れ)はdelete()
メソッドで行います。
try { await cookieStore. delete ( 'session_id' ); } catch ( e) { console. error( `クッキーの削除に失敗: ${ e} ` ); }
内部的には、削除はクッキーの有効期限を過去に変更することで実現されます。
try { const one_day_ms= 24 * 60 * 60 * 1000 ; await cookieStore. set({ name: 'session_id' , value: 'value will be ignored' , expires: Date. now() - one_day_ms}); } catch ( e) { console. error( `クッキーの削除に失敗: ${ e} ` ); }
1.5. クッキーの監視
ポーリングを避けるため、クッキーの変更を監視できます。
ドキュメントでは、すべての関連クッキー変更に対してchange
イベントが発火されます。
change
イベントを登録:
cookieStore. addEventListener( 'change' , event=> { console. log( ` ${ event. changed. length} 件のクッキーが変更されました` ); for ( const cookiein event. changed) console. log( `クッキー ${ cookie. name} が ${ cookie. value} に変更されました` ); console. log( ` ${ event. deleted. length} 件のクッキーが削除されました` ); for ( const cookiein event. deleted) console. log( `クッキー ${ cookie. name} が削除されました` ); });
Service
Workerでは、グローバルスコープにcookiechange
イベントが発火されますが、Service Workerの登録ごとに明示的な購読が必要です。
cookiechange
イベントを登録:
self. addEventListener( 'activate' , ( event) => { event. waitUntil( async () => { // 現在の購読状態をスナップショット const subscriptions= await self. registration. cookies. getSubscriptions(); // 既存の購読をすべて解除 await self. registration. cookies. unsubscribe( subscriptions); await self. registration. cookies. subscribe([ { name: 'session_id' , // session_idという名前のクッキー変更イベントを受信 } ]); }); }); self. addEventListener( 'cookiechange' , event=> { // eventの|changed|や|deleted|プロパティはDocumentイベントと同じ意味 console. log( ` ${ event. changed. length} 件のクッキーが変更されました` ); console. log( ` ${ event. deleted. length} 件のクッキーが削除されました` ); });
subscribe()
の呼び出しは累積されるため、独立したモジュールやライブラリがそれぞれ購読を設定できます。Service
Workerの購読状態はService Worker登録に対して永続化されます。
購読はget()
やgetAll()
と同じオプションを使えます。
Service Workerへの無関係なクッキー変更イベントの配送コストが高いため、細かい購読の複雑さは正当化されます。
Window
への配送コストより、Service Workerへの配送はWorkerの起動が必要な場合もあり、バッテリー消費に大きな影響があります。
getSubscriptions()
は、Service Workerで購読状況を確認できます。
const subscriptions= await self. registration. cookies. getSubscriptions(); for ( const subof subscriptions) { console. log( sub. name, sub. url); }
2. 概念
2.1. クッキー
クッキーは、ユーザーエージェント向けにCookies § User Agent Requirementsで規定されています。
クッキー名または値を正規化するには、string inputが与えられた場合、 inputの先頭または末尾にあるU+0009 TABおよびU+0020 SPACEをすべて削除する。
クッキーは、スコープ内かつhttp-only-flagが未設定の時、スクリプトから見える状態です。これはより厳密には処理モデルで強制され、適切なタイミングでCookies § Retrieval Modelを参照します。
クッキーにはサイズ制限もあります。Cookies § Storage Modelによると:
-
nameとvalueフィールドの合計長は4096バイト(name/valueペア最大サイズ)を超えてはなりません。
-
nameとvalue以外の各フィールドの長さは1024バイト(属性値最大サイズ)を超えてはなりません。
クッキーの属性値はバイト列として保存され、文字列ではありません。
2.2. クッキーストア
クッキーストアは、ユーザーエージェント向けにCookies § User Agent Requirementsで規定されています。
クッキーストアに対して以下のいずれかが発生したときは、クッキー変更処理を実行します。
2.3. Service Workerへの拡張
[Service-Workers]はService Worker登録を定義しており、本仕様はこれを拡張します。
Service Worker登録には関連付けられたクッキー変更購読リストがあり、これはリストです。 各メンバーはクッキー変更購読です。クッキー変更購読は タプル(組)で、 nameとurl から成ります。
3. CookieStore
インターフェイス
[Exposed =(ServiceWorker ,Window ),SecureContext ]interface :
CookieStore EventTarget {Promise <CookieListItem ?>get (USVString );
name Promise <CookieListItem ?>get (optional CookieStoreGetOptions = {});
options Promise <CookieList >getAll (USVString );
name Promise <CookieList >getAll (optional CookieStoreGetOptions = {});
options Promise <undefined >set (USVString ,
name USVString );
value Promise <undefined >set (CookieInit );
options Promise <undefined >delete (USVString );
name Promise <undefined >delete (CookieStoreDeleteOptions ); [
options Exposed =Window ]attribute EventHandler ; };
onchange dictionary {
CookieStoreGetOptions USVString ;
name USVString ; };
url enum {
CookieSameSite ,
"strict" ,
"lax" };
"none" dictionary {
CookieInit required USVString ;
name required USVString ;
value DOMHighResTimeStamp ?=
expires null ;USVString ?=
domain null ;USVString = "/";
path CookieSameSite = "strict";
sameSite boolean =
partitioned false ; };dictionary {
CookieStoreDeleteOptions required USVString ;
name USVString ?=
domain null ;USVString = "/";
path boolean =
partitioned false ; };dictionary {
CookieListItem USVString ;
name USVString ; };
value typedef sequence <CookieListItem >;
CookieList
3.1. get()
メソッド
- cookie = await cookieStore .
get
(name)- cookie = await cookieStore .
get
(options) - cookie = await cookieStore .
-
指定したクッキー名(または他のオプション)に対して、スコープ内の最初のスクリプトから見える値を解決するPromiseを返します。 Service Workerのコンテキストでは、Service Workerの登録スコープのパスがデフォルトになります。 ドキュメントでは、現在のドキュメントのパスがデフォルトとなり、
replaceState()
やdocument.domain
での変更は反映されません。
get(name)
メソッドの手順:
-
settingsに、thisの関連設定オブジェクトを設定する。
-
originに、settingsのオリジンを設定する。
-
originが不透明オリジンの場合、"
SecurityError
"DOMException
でPromiseを拒否して返す。 -
urlに、settingsの作成URLを設定する。
-
pに、新しいPromiseを設定する。
-
以下の手順を並行して実行:
-
pを返す。
get(options)
メソッドの手順:
-
settingsに、thisの関連設定オブジェクトを設定する。
-
originに、settingsのオリジンを設定する。
-
originが不透明オリジンなら、"
SecurityError
"DOMException
でPromiseを拒否して返す。 -
urlに、settingsの作成URLを設定する。
-
optionsが空なら、
TypeError
でPromiseを拒否して返す。 -
options["
url
"] が存在する場合、以下の手順を実行:-
thisの関連グローバルオブジェクトが
Window
かつparsedがurlと等しくない場合(exclude fragmentsをtrue)、TypeError
でPromiseを拒否して返す。 -
parsedのoriginとurlのoriginが同一オリジンでない場合、
TypeError
でPromiseを拒否して返す。 -
urlにparsedを設定する。
-
pに新しいPromiseを設定する。
-
以下の手順を並行して実行:
-
pを返す。
3.2. getAll()
メソッド
- cookies = await cookieStore .
getAll
(name)- cookies = await cookieStore .
getAll
(options) - cookies = await cookieStore .
-
指定したクッキー名(または他のオプション)に対して、スコープ内のすべてのスクリプトから見える値を解決するPromiseを返します。 Service Workerのコンテキストでは、Service Workerの登録スコープのパスがデフォルトとなります。 ドキュメントでは、現在のドキュメントのパスがデフォルトとなり、
replaceState()
やdocument.domain
の変更は反映されません。
getAll(name)
メソッドの手順:
-
settingsに、thisの関連設定オブジェクトを設定する。
-
originに、settingsのオリジンを設定する。
-
originが不透明オリジンの場合、"
SecurityError
"DOMException
でPromiseを拒否して返す。 -
urlに、settingsの作成URLを設定する。
-
pに、新しいPromiseを設定する。
-
以下の手順を並行して実行:
-
pを返す。
getAll(options)
メソッドの手順:
-
settingsに、thisの関連設定オブジェクトを設定する。
-
originに、settingsのオリジンを設定する。
-
originが不透明オリジンの場合、"
SecurityError
"DOMException
でPromiseを拒否して返す。 -
urlに、settingsの作成URLを設定する。
-
options["
url
"] が存在する場合、以下の手順を実行:-
thisの関連グローバルオブジェクトが
Window
かつparsedがurlと等しくない場合(exclude fragmentsをtrue)、TypeError
でPromiseを拒否して返す。 -
parsedのoriginとurlのoriginが同一オリジンでない場合、
TypeError
でPromiseを拒否して返す。 -
urlにparsedを設定する。
-
pに新しいPromiseを設定する。
-
以下の手順を並行して実行:
-
pを返す。
3.3. set()
メソッド
- await cookieStore .
set
(name, value)- await cookieStore .
set
(options) - await cookieStore .
-
クッキーを書き込みます(作成または変更)。
オプションのデフォルト値:
-
Path:
/
-
Domain: 現在のドキュメントまたはService Workerの場所と同じドメイン
-
有効期限なし
-
SameSite: strict
-
set(name, value)
メソッドの手順:
-
settingsに、thisの関連設定オブジェクトを設定する。
-
originに、settingsのオリジンを設定する。
-
originが不透明オリジンの場合、"
SecurityError
"DOMException
でPromiseを拒否して返す。 -
urlに、settingsの作成URLを設定する。
-
domainにnullを設定する。
-
pathに"/"を設定する。
-
sameSiteに
strict
を設定する。 -
partitionedにfalseを設定する。
-
pに新しいPromiseを設定する。
-
以下の手順を並行して実行:
-
pを返す。
set(options)
メソッドの手順:
-
settingsに、thisの関連設定オブジェクトを設定する。
-
originに、settingsのオリジンを設定する。
-
originが不透明オリジンなら、"
SecurityError
"DOMException
でPromiseを拒否して返す。 -
urlに、settingsの作成URLを設定する。
-
pに新しいPromiseを設定する。
-
以下の手順を並行して実行:
-
pを返す。
3.4. delete()
メソッド
- await cookieStore .
delete
(name)- await cookieStore .
delete
(options) - await cookieStore .
-
指定した名前、または名前+オプションのドメインとパスで、クッキーを削除(有効期限切れ)します。
delete(name)
メソッドの手順:
-
settingsに、thisの関連設定オブジェクトを設定する。
-
originに、settingsのオリジンを設定する。
-
originが不透明オリジンの場合、"
SecurityError
"DOMException
でPromiseを拒否して返す。 -
urlに、settingsの作成URLを設定する。
-
pに新しいPromiseを設定する。
-
以下の手順を並行して実行:
-
pを返す。
delete(options)
メソッドの手順:
-
settingsに、thisの関連設定オブジェクトを設定する。
-
originに、settingsのオリジンを設定する。
-
originが不透明オリジンの場合、"
SecurityError
"DOMException
でPromiseを拒否して返す。 -
urlに、settingsの作成URLを設定する。
-
pに新しいPromiseを設定する。
-
以下の手順を並行して実行:
-
pを返す。
4. CookieStoreManager
インターフェイス
CookieStoreManager
には、関連付けられたregistration(Service Worker登録)があります。
CookieStoreManager
インターフェイスは、Service Workerがクッキー変更イベントを購読できるようにします。subscribe()
メソッドの利用で、特定のService Worker登録が変更イベントに関心があることを示します。
[Exposed =(ServiceWorker ,Window ),SecureContext ]interface {
CookieStoreManager Promise <undefined >subscribe (sequence <CookieStoreGetOptions >);
subscriptions Promise <sequence <CookieStoreGetOptions >>getSubscriptions ();Promise <undefined >unsubscribe (sequence <CookieStoreGetOptions >); };
subscriptions
4.1.
subscribe()
メソッド
- await registration . cookies .
subscribe
(subscriptions) -
クッキーの変更を購読します。購読は
get()
やgetAll()
と同じオプション(name
やurl
プロパティ)を使えます。購読後は、通知が
cookiechange
イベントとしてService Workerのグローバルスコープで発火されます。
subscribe(subscriptions)
メソッドの手順:
-
settingsに、thisの関連設定オブジェクトを設定。
-
registrationに、thisのregistrationを設定。
-
pに新しいPromiseを設定。
-
以下の手順を並行して実行:
-
subscription listをregistrationに関連付けられたクッキー変更購読リストとする。
-
各entryをsubscriptions内で順に、次の手順を実行する:
-
nameをentry["
name
"]とする。 -
もしurlがregistrationのスコープURLで始まらない場合、pをTypeErrorで拒否し、これ以降の手順を中止する。
-
subscriptionをクッキー変更購読(name, url)とする。
-
もしsubscription listがすでにsubscriptionを含まない場合、subscriptionをsubscription listに追加する。
-
-
-
pを返す。
4.2.
getSubscriptions()
メソッド
- subscriptions = await registration . cookies .
getSubscriptions()
-
このメソッドは、このService Worker登録に対して行われたクッキー変更購読のリストを返すPromiseを返します。
getSubscriptions()
メソッドの手順:
-
registrationに、thisのregistrationを設定。
-
pに新しいPromiseを設定。
-
以下の手順を並行して実行:
-
subscriptionsにregistrationの関連付けられたクッキー変更購読リストを設定する。
-
resultに新しいリストを設定する。
-
各subscriptionについてsubscriptionsを順に処理:
-
-
pを返す。
4.3.
unsubscribe()
メソッド
- await registration . cookies .
unsubscribe
(subscriptions) -
このメソッドを呼ぶと、登録済みService Workerは以前購読していたイベントの受信を停止します。 subscriptions引数は、
subscribe()
で渡したもの、またはgetSubscriptions()
で返されたものと同じ形式であるべきです。
unsubscribe(subscriptions)
メソッドの手順:
-
settingsに、thisの関連設定オブジェクトを設定。
-
registrationに、thisのregistrationを設定。
-
pに新しいPromiseを設定。
-
以下の手順を並行して実行:
-
subscription listにregistrationの関連付けられたクッキー変更購読リストを設定する。
-
各entryについてsubscriptionsを順に処理:
-
nameをentry["
name
"]とする。 -
もしurlがregistrationのスコープURLで始まらない場合、pをTypeErrorで拒否し、これ以降の手順を中止する。
-
subscriptionをクッキー変更購読(name, url)とする。
-
-
-
pを返す。
4.4.
ServiceWorkerRegistration
インターフェイス
ServiceWorkerRegistration
インターフェイスは、CookieStoreManager
へのアクセスを提供するよう拡張されています。cookies
プロパティ経由で、クッキー変更購読用インターフェイスが使えます。
[Exposed =(ServiceWorker ,Window )]partial interface ServiceWorkerRegistration { [SameObject ]readonly attribute CookieStoreManager cookies ; };
各ServiceWorkerRegistration
には関連するCookieStoreManager
オブジェクトがあります。
CookieStoreManager
の
registrationは、ServiceWorkerRegistration
の
Service Worker登録と等しいです。
cookies
ゲッターの手順は、thisの関連CookieStoreManager
オブジェクトを返すことです。
navigator. serviceWorker. register( 'sw.js' ). then( registration=> { registration. cookies. subscribe([{ name: 'session-id' }]); });
5. イベントインターフェイス
5.1. CookieChangeEvent
インターフェイス
CookieChangeEvent
は、CookieStore
オブジェクトに対して、Window
コンテキストで、スクリプトから見えるクッキーが変更されたときにdispatchされます。
[Exposed =Window ,SecureContext ]interface :
CookieChangeEvent Event {(
constructor DOMString ,
type optional CookieChangeEventInit = {}); [
eventInitDict SameObject ]readonly attribute FrozenArray <CookieListItem >; [
changed SameObject ]readonly attribute FrozenArray <CookieListItem >; };
deleted dictionary :
CookieChangeEventInit EventInit {CookieList ;
changed CookieList ; };
deleted
changed
とdeleted
属性は初期化時の値を返さなければなりません。
5.2.
ExtendableCookieChangeEvent
インターフェイス
ExtendableCookieChangeEvent
は、ServiceWorkerGlobalScope
オブジェクトに対して、スクリプトから見える
クッキーの変更が発生し、その変更がService Workerの
クッキー変更購読リストに一致する場合にdispatchされます。
注: ExtendableEvent
は、Service
Worker内のすべてのイベントの祖先インターフェイスとして使われており、非同期処理中にWorker自体を存続させるためです。
[Exposed =ServiceWorker ]interface :
ExtendableCookieChangeEvent ExtendableEvent {(
constructor DOMString ,
type optional ExtendableCookieChangeEventInit = {}); [
eventInitDict SameObject ]readonly attribute FrozenArray <CookieListItem >; [
changed SameObject ]readonly attribute FrozenArray <CookieListItem >; };
deleted dictionary :
ExtendableCookieChangeEventInit ExtendableEventInit {CookieList ;
changed CookieList ; };
deleted
changed
とdeleted
属性は初期化時の値を返さなければなりません。
6. グローバルインターフェイス
CookieStore
には、グローバルスコープの属性として、Window
またはServiceWorkerGlobalScope
コンテキストで、スクリプトからアクセスできます。
6.1. Window
インターフェイス
[SecureContext ]partial interface Window { [SameObject ]readonly attribute CookieStore cookieStore ; };
Window
には、関連付けられたCookieStoreがあり、これはCookieStore
です。
cookieStore
ゲッターの手順は、thisの関連付けられたCookieStoreを返すことです。
6.2. ServiceWorkerGlobalScope
インターフェイス
partial interface ServiceWorkerGlobalScope { [SameObject ]readonly attribute CookieStore cookieStore ;attribute EventHandler ; };
oncookiechange
ServiceWorkerGlobalScope
には、関連付けられたCookieStoreがあり、これはCookieStore
です。
cookieStore
ゲッターの手順は、thisの関連付けられたCookieStoreを返すことです。
7. アルゴリズム
注: これはECMAScriptの[ECMAScript]で使われている表現と同じです。
DOMHighResTimeStamp
millisを扱うには、
dateTimeを1970年1月1日00:00:00 UTCからmillisミリ秒後の日時とし(1日=86,400,000ミリ秒とする)、
そのdateTimeの最も近いcookie-date
表現に対応するバイト列を返す(Cookies § Dates参照)。
7.1. クッキーの検索
クッキーを照会するには、URL urlと、stringまたはnullのnameが与えられる:
-
Cookies § 取得モデルで定義された手順を実行し、urlをrequest-uriとして「cookie-string from a given cookie store」を算出する。cookie-string自体は無視し、中間のcookie-listを以降の手順で利用する。
この手順では、cookie-stringは「非HTTP」APIのために生成される。
-
listを新しいリストとする。
-
各cookieをcookie-listで順に、次の手順を実行する:
-
Assert: cookieのhttp-only-flagはfalseである。
-
もしnameがnullでない場合:
-
cookieNameを、cookieのnameにBOMなしUTF-8デコードを実行した結果とする。
-
もしcookieNameがnameと一致しない場合、continueする。
-
itemをcreate a CookieListItemでcookieから生成した結果とする。
-
itemをlistに追加する。
-
-
listを返す。
CookieListItem
を生成するには、cookie cookieが与えられる:
-
nameを、cookieのnameにBOMなしUTF-8デコードを実行した結果とする。
-
valueを、cookieのvalueにBOMなしUTF-8デコードを実行した結果とする。
Note: 1つの実装は_name_と_value_以外の情報も公開することが知られている。
7.2. クッキーの設定
クッキーを設定するには、 url、 name、 value、 任意のexpires、 domain、 path、 sameSite、 partitioned を用いて、以下の手順を実行する:
-
もしnameまたはvalueがU+003B(;)、U+0009 TAB以外のC0制御文字、またはU+007F DELETEを含む場合、失敗を返す。
この文字制限がexpires、domain、path、sameSiteにも適用されるかどうかは議論中。[httpwg/http-extensions Issue #1593]
-
もしnameがU+003D(=)を含む場合、失敗を返す。
-
もしnameの長さが0の場合:
-
encodedNameをnameにUTF-8エンコードした結果とする。
-
encodedValueをvalueにUTF-8エンコードした結果とする。
-
encodedNameのバイト系列の長さと、encodedValueのバイト系列の長さの合計が最大name/valueペアサイズを超える場合、失敗を返す。
-
hostをurlのホストとする。
-
attributesを新しいリストとする。
-
もしdomainがnullでない場合、次の手順を実行する:
-
もしdomainがU+002E(.)で始まる場合、失敗を返す。
-
もしdomainがhostの登録可能なドメインサフィックスでなく、等しくもない場合、失敗を返す。
-
parsedDomainをdomainにホストパースした結果とする。
-
Assert: parsedDomainは失敗ではない。
-
encodedDomainをparsedDomainにUTF-8エンコードした結果とする。
-
-
expires が与えられている場合、append `
Expires
`/expires(日付の直列化)を attributes に追加する。 -
path が空文字列の場合、path に 直列化された Cookie のデフォルトパス(url のもの)を設定する。
-
path が U+002F(/)で始まらない場合、失敗を返す。
-
path が U+002F(/)でない場合、かつ name を バイト小文字化し、`__host-` で始まる場合、失敗を返す。
-
encodedPath を UTF-8 エンコードした path とする。
-
sameSite に応じて切り替える:
-
partitioned が true の場合、`Partitioned`/`` を attributes に追加する。
-
Cookies § Storage Model に定義されている手順を、ユーザーエージェントが「Cookie を受け取った」ときについて、 url を request-uri として、 encodedName を cookie-name として、 encodedValue を cookie-value として、 attributes を cookie-attribute-list として実行する。
これらの手順において、新しく作成された Cookie は「非 HTTP」API から受け取ったものとみなす。
-
成功を返す。
注: Cookie の保存は [RFC6265BIS-14] の要件により失敗する場合があるが、 これらの手順は成功したものとみなされる。
7.3. クッキーの削除
クッキーを削除するには、 url、 name、 domain、 path、 partitioned を用いて、以下の手順を実行する:
-
expiresを、タイムスタンプとして表される表現可能な最も早い日付とする。
Note: このアルゴリズムの目的上、expiresの正確な値は重要ではなく、過去の日付であればよい。
-
valueを空文字列とする。
-
set a cookieを url、 name、 value、 expires、 domain、 path、 "
strict
"、 partitioned で実行した結果を返す。
7.4. 変更の処理
クッキー変更処理を行うには、次の手順を実行:
-
すべての
Window
windowについて、以下を実行:-
urlにwindowの関連設定オブジェクトの作成URLを設定する。
-
changesにurlの観測可能変更を設定する。
-
グローバルタスクをキューに追加して、DOM操作タスクソースでwindowに対し、 changeイベント発火(名前"
change
"、changes、windowのCookieStore
)を行う。
-
-
すべてのService Worker登録 registrationについて、以下を実行:
-
changesに新しいセットを設定。
-
各changeについて、registrationの観測可能変更(registrationのscope url)を順に処理:
-
cookieにchangeのクッキーを設定。
-
各subscriptionについてregistrationのクッキー変更購読リストを順に処理:
-
cookieNameにcookieのnameをUTF-8 BOMなしでデコードした結果を設定。
-
cookieNameがsubscriptionのnameと一致すれば、changeをchangesに追加して、breakする。
-
-
changedListとdeletedListにchangesからリスト準備を実行した結果を設定。
-
機能イベント発火(名前"
cookiechange
"、ExtendableCookieChangeEvent
、registration)を、下記プロパティで行う:
-
urlの観測可能変更は、セットであり、クッキー変更で、クッキーがクッキーストア内にあり、Cookies § Retrieval Algorithmの「cookie-string from a given cookie store」算出手順の1番に合致し、urlをrequest-uri、非HTTP APIとする。
クッキー変更は、 クッキーとタイプ(changedまたはdeleted)の組み合わせ:
typeという名前で、changesをtargetでchangeイベント発火するには、次の手順を行う:
-
eventに
CookieChangeEvent
でEvent生成を設定。 -
eventの
type
属性にtypeを設定。 -
eventの
bubbles
とcancelable
属性をfalseに設定。 -
changedListとdeletedListに、changesからリスト準備した結果を設定。
-
eventの
changed
属性にchangedListを設定。 -
eventの
deleted
属性にdeletedListを設定。
changesからリスト準備を行うには、次の手順:
-
changedListに新しいリストを設定。
-
deletedListに新しいリストを設定。
-
各changeについてchangesを順に処理:
-
itemにchangeのクッキーからCookieListItem生成を実行した結果を設定。
-
changeのtypeがchangedなら、itemをchangedListに追加。
-
それ以外の場合、以下を実行:
-
item["
value
"]にundefinedを設定。
-
-
-
changedListとdeletedListを返す。
8. セキュリティに関する考慮事項
Service Workerコンテキストからのクッキーアクセス以外、このAPIはウェブに新たな機能を公開することを意図していません。
8.1. 注意点!
ブラウザのクッキー実装は現在より良いセキュリティと予想外・誤りやすいデフォルトの削減に進化していますが、現状クッキーデータのセキュリティについてはほとんど保証がありません。
-
非セキュアなオリジンが、セキュアなオリジンで利用されるクッキーを上書きできる場合が多い
-
スーパー・ドメインがサブドメインで見えるクッキーを上書きできる場合が多い
-
クロスサイトスクリプティング攻撃やその他のスクリプト/ヘッダインジェクション攻撃でクッキーを偽造することも可能
-
クッキーの読み取り(スクリプト/ウェブサーバーの両方)は、そのクッキーがどこから来たか示唆しません
-
ブラウザは時にクッキーデータを予想外・直感に反する方法で切り詰め・変換・退去させる場合がある
-
... 記憶域制限に達した場合
-
... 文字コードの違いによる場合
-
... クッキーの構文・意味規則の違いによる場合
-
これらの理由から、クッキーの値を解釈する際は注意し、クッキーの値をスクリプト、HTML、CSS、XML、PDF、その他実行可能な形式として決して実行しないでください。
8.2. 制限?
このAPIはクッキー利用を容易にし、結果としてクッキー利用をさらに促進する副作用があるかもしれません。非セキュアなコンテキストでクッキー利用が促進されると、ユーザーにとって安全性が低下するウェブになる可能性があります。そのため、このAPIはセキュアなコンテキスト限定としています。
8.3. セキュアクッキー
このセクションは規定ではありません。
このAPIはセキュリティ向上のためSecure
クッキーのみ書き込み可能です。ただし、Secure
でないクッキーも読み取り可能で、Secure
クッキーへの移行を容易にしています。副作用として、このAPIで非Secure
クッキーを取得・変更した場合、そのクッキーは自動的にSecure
に変更されます。
8.4. 驚き
既存のクッキー挙動(特にドメイン志向、非セキュアなコンテキストからSecure
コンテキストで読めるクッキーを設定できること、スクリプトから読めないクッキーをスクリプトで設定できること等)は、ウェブセキュリティ観点でかなり驚くべきものです。
その他の驚きはCookies § Introductionでも記載されており、例えばクッキーはスーパー・ドメイン(例: app.example.comがexample.com全体)に設定できる、クッキーはドメイン名の任意ポート番号で読める等があります。
さらに主要ブラウザ間のクッキー処理の歴史的な違いも複雑化要因ですが、最近は(例:ポート番号処理)は以前より一貫性が向上しています。
8.5. プレフィックス
可能な限り、例では__Host-
と__Secure-
の名前プレフィックスを使っています。これにより現行ブラウザの一部では、非セキュアなコンテキストからの上書き、Secure
フラグなし上書き、さらに__Host-
の場合は明示的なDomain
や非'/'
Path
属性での上書き(事実上same-origin制約)が禁止されます。これらプレフィックスはSecure
Cookieを実装しているブラウザで重要なセキュリティ効果を発揮し、他のブラウザでは劣化動作(特別な意味は強制しないがクッキー自体は通常通り動作、非同期APIではセキュアな意味を強制)します。本APIの大きな目的は既存クッキーとの相互運用なので、プレフィックス無しの例も一部記載しています。
プレフィックスの規則はこのAPIの書き込み操作でも強制されますが、他のAPIでは必ずしも同じ規則が適用されるとは限りません。そのため、規則の強制を過度に信頼するのは推奨されません(広範な採用がされるまで)。
8.6. URLスコープ
現状ではService Workerスクリプトから直接クッキーにアクセスできませんが、Service Workerが支配するHTMLやスクリプトリソースを制御描画することで、クッキー監視コードをリモート制御下に挿入でき、技術的にはService Workerのスコープ内でクッキーアクセスが可能です(使い勝手が悪いだけ)。
Service
Workerのスコープが/
より狭い場合でも、スコープ外パスのクッキーを404ページURLのIFRAME化などでスクリプトを実行し、読み取れる場合があります。同様の技術でオリジン全体にも拡張可能ですが、IFRAME化できないよう設計したサイトなら、パススコープのService
Workerがこの機能を拒否できます。この制約の削除には追加議論が必要と考えています。
8.7. クッキー嫌い
開発者の複雑性削減と一時的なテストクッキー不要化のため、この非同期クッキーAPIは無視される操作の書き込み・削除を明示的に拒否します。同様に、実際のクッキーデータを無視し空のクッキーjarを模擬するような読み取りも明示的に拒否します。こうしたコンテキストでの変更監視は「動作」しますが、実際の読み取りが許可されるまでコールバックは呼ばれません(例:サイト権限変更時など)。
現状、document.cookie
でスクリプトによるクッキー書き込み不可コンテキストでは通常何も起こりません。ただし、多くのスクリプトやフレームワークは必ずテストクッキーを書き、存在確認で書き込み可否を判定しています。
同様に、document.cookie
で読み取り不可コンテキストでは通常空文字列が返ります。協力的なウェブサーバーはサーバー主導のクッキー書き込み・読み取りが機能することを検証し、スクリプトに報告できます(スクリプトは空文字列しか見えないが、これで読み取り不可を判別可能)。
9. プライバシーに関する考慮事項
9.1. クッキーの消去
このセクションは規定ではありません。
ユーザーがあるオリジンのクッキーを消去した場合、ユーザーエージェントはそのオリジンのすべてのストレージ(Service Worker・DOMストレージ含む)も消去する必要があります。これは、ウェブサイトが消去後に永続ストレージでユーザー識別子を復元するのを防ぐためです。
謝辞
このAPIの初期提案者Benjamin Sittlerに感謝します。
Adam Barth、 Alex Russell、 Andrea Marchesini、 Andrew Williams、 Anne van Kesteren、 Ayu Ishii、 Ben Kelly、 Craig Francis、 Daniel Appelquist、 Daniel Murphy、 Domenic Denicola、 Elliott Sprehn、 Fagner Brack、 Idan Horowitz、 Jake Archibald、 Joel Weinberger、 Joshua Bell、 Kenneth Rohde Christiansen、 Lukasz Olejnik、 Marijn Kruisselbrink、 Mike West、 Raymond Toy、 Rupin Mittal、 Tab Atkins、 Victor Costan ほか、多くの方々に現行標準策定の支援をいただきました。
この現行標準はDylan Cutler(Google, dylancutler@google.com)によって執筆されています。
知的財産権
Copyright © WHATWG(Apple, Google, Mozilla, Microsoft)。この成果物はクリエイティブ・コモンズ 表示 4.0 国際ライセンスの下でライセンスされています。ソースコードへ組み込まれる部分については、ソースコード内でBSD 3-Clause Licenseの下でライセンスされます。