W3C - 『Web Storage』日本語訳

一部、直訳ではなく意訳した部分がございます。原文と表現が異なることがございますので、ご了承ください。この日本語訳は、私が理解を深めるために、自分なりに日本語化したものです。本日本語訳には、翻訳上の誤りがある可能性があります。したがって、内容について一切保証をするものではありません。正確さを求める場合には、必ず原文を参照してください。当方は、この文書によって利用者が被るいかなる損害の責任を負いません。もし誤りなどを見つけたら、当サイトのお問い合わせより連絡いただければ幸いです。

概要

Web Storage とは、データをブラウザ側に蓄積する仕組みです。クッキーとよく似ていますが、多くの点で異なります。Web Storage は大きなデータを蓄積できますので、ユーザーエクスペリエンスの向上やサーバー負荷の低減に役立てることができます。

現在は W3C の HTML5 仕様から分離されてしまいましたが、次世代のウェブアプリケーションの中核を担うテクノロジーとして期待されています。


要約

本仕様は、ウェブ・クライアントに key-value ペアの永続的なデータを蓄積するための API を定義します。

本文書の状態

このセクションでは、発行時における本文書の状態を説明します。本文書は他の文書によって置き換えられるかもしれません。現在のW3Cの発行物と、この技術に関するレポートの最新の公式の公開リビジョンの一覧は、http://www.w3.org/TR/ の W3C technical reports index をご覧ください。

実装者は、この仕様はまだ安定していないということを承知するべきです。この議論に参加していない実装者は、この仕様を逸脱して、互換性がなくなってしまうような解釈をしてしまうかもしれません。勧告候補の段階に達する前に、この仕様の実装に興味を持ったベンダーは、前述のメーリングリストに加わって、議論に参加するべきです。

本文書に関して W3C に見てもらえるようコメントを希望するなら、我々の公開バグ・データベースを経由して投稿してください。アカウントをお持ちでないなら、このフォームを使ってフィードバックを入れてください:

また、public-webapps@w3.org (subscribe, archives) や whatwg@whatwg.org (subscribe, archives) にフィードバックをメールすることもできます。どんなフィードバックでも歓迎です。

本仕様の編集者草案の最新の安定バージョンは、W3C の CVS サーバWHATWG の Subversion リポジトリ でいつでも入手可能です。最新の編集者作業版(準備中のため未完成のテキストも含まれるかもしれません) には、本仕様の最新の草案テキストが含まれています(その他も含む)。詳細は WHATWG FAQ をご覧ください。

本仕様の変更の通知は、次のメカニズムを使って、関連の仕様の変更の通知とともに送られます:

E-mail による変更通知
Commit-Watchers mailing list (complete source diffs): http://lists.whatwg.org/listinfo.cgi/commit-watchers-whatwg.org
全変更点のバージョン管理記録:
CVSWeb interface with side-by-side diffs: http://dev.w3.org/cvsweb/html5/
Annotated summary with unified diffs: http://html5.org/tools/web-apps-tracker
Raw Subversion interface: svn checkout http://svn.whatwg.org/webapps/

W3C の Web Apps ワーキンググループは、W3C 勧告トラックに沿ってこの仕様の進捗に責任を持つ W3C ワーキンググループです。この仕様は、2011年 2 月 8 日版の草案です。

本文書は、5 February 2004 W3C Patent Policy に基づき運営するグループによって作られました。W3C は、グループの成果物に関連して作られた public list of any patent disclosures をメンテナンスしています。そのページには、特許を開示する方法も掲載されています。Essential Claim(s) を含むと信じる特許の実知識を持つ個人は、section 6 of the W3C Patent Policy に従って情報を開示しなければいけません。

課題

今のところ、ある実装者によると、競合状態を回避するためにストレージ排他制御を使おうとすると、パフォーマンスにとって大きな重荷になると考えられおり、データ破損が許されることのほうが望ましいと考えれています。後に、origin ごとにユーザーエージェント全体でスクリプトをロックする必要がないという選択肢が熱心に探求されています。もし提案があれば、ぜひ、前のセクションに示したアドレスに送ってください。

本課題に関しての詳細は、次のメールから入手可能です(他にもたくさんあります):

目次

  1. 1 はじめに
  2. 2 準拠要件
    1. 2.1 依存関係
  3. 3 用語の定義
  4. 4 API
    1. 4.1 Storage インタフェース
    2. 4.2 sessionStorage 属性
    3. 4.3 localStorage 属性
      1. 4.3.1 セキュリティー
    4. 4.4 storage イベント
      1. 4.4.1 イベント定義
    5. 4.5 スレッド
  5. 5 ディスク・スペース
  6. 6 プライバシー
    1. 6.1 ユーザー追跡
    2. 6.2 データの機密性
  7. 7 セキュリティー
    1. 7.1 DNS スプーフィング・アタック
    2. 7.2 クロス・ディレクトリ・アタック
    3. 7.3 実装のリスク
  8. リファレンス
  9. 謝辞

1 はじめに

このセクションは非準拠です。

この仕様では 2 つの関連メカニズムを紹介します。それは、HTTP セッション・クッキーに似ていますが、クライアント側で構造化データを蓄積するものです。[COOKIES]

1つ目は、ユーザーが単一のトランザクションを実行しているところを想定して設計されたものですが、異なるウィンドウで複数のトランザクションを同時に実行することができます。

クッキーでは、この場合をうまく扱うことができません。例えば、ユーザーが、同じサイトを使って、2 つの異なるウィンドウで飛行機のチケットを購入しようとする場合です。そのユーザーがどのチケットを購入しようとしていたのかを保持するために、そのサイトがクッキーを使っていた場合、そのユーザーが両方のウィンドウにあるそれぞれのページをクリックしたら、その時点で購入しようとしているチケットは、一方のウィンドウからもう一方のウィンドウに "漏れて" しまうでしょう。ユーザーはそれに全く気づかずに、同じフライトのチケットを2つ購入してしまうことになりかねません。

この問題に対処するために、この仕様は sessionStorage IDL 属性を取り入れています。サイトは、セッション・ストレージにデータを加えることができ、そのウィンドウで開かれている同じサイトのどんなページからでもアクセスすることができるようになります。

例えば、保険が欲しい場合にユーザーがチェックを入れるチェックボックスをページに入れることができるでしょう:

<label>
 <input type="checkbox" onchange="sessionStorage.insurance = checked">
 私はこの旅行で保険が欲しいです。
</label>

その後のページでは、ユーザーがチェックボックスをチェックしたかどうかをスクリプトから判別することができるようになります:

if (sessionStorage.insurance) { ... }

ユーザーがそのサイトでいくつもウィンドウを開いていたなら、それぞれのページごとに、セッション・ストレージ・オブジェクトのコピーを別々に持つことになります。

2 つ目のストレージ・メカニズムは、複数のウィンドウをまたがり、そして、現在のセッションが終了しても存続するストレージを想定したものです。特に、パフォーマンスの理由で、クライアント側に、ユーザーが編集したドキュメントを丸ごと、とか、ユーザーのメールボックスのように、何メガバイトものユーザーデータを蓄積したいと考えるウェブアプリケーションがあるでしょう。

何度も言うようですが、クッキーでは、この場合をうまく対処できません。なぜなら、リクエストの度に送信されてしまうからです。

localStorage IDL 属性を使って、ページのローカルストレージ領域にアクセスします。

example.com のサイトは、ユーザーがそのページを何回ロードしたかを表示することができます。そのページの最後に次のコードを入れます:

<p>
  You have viewed this page
  <span id="count">an untold number of</span>
  time(s).
</p>
<script>
  if (!localStorage.pageLoadCount)
    localStorage.pageLoadCount = 0;
  localStorage.pageLoadCount += 1;
  document.getElementById('count').textContent = localStorage.pageLoadCount;
</script>

サイトごとに別々のストレージ領域が用意されます。

2 準拠要件

この仕様では、それぞれの節で非規定と記載されていれば、その節にある図表や例や注意事項はすべて非規定です。それ以外はすべて規定です。

この仕様の規定部分にある "しなければいけない(MUST)", "してはいけない(MUST NOT)", "する必要がある(REQUIRED)", "するべきである(SHOULD)", "すべきではない(SHOULD NOT)", "推奨される(RECOMMENDED)", "しても構わない(MAY)", "オプションで(OPTIONAL)" というキーワードは、RFC2119 の規定通りに解釈してください。 [RFC2119]

アルゴリズムの一部として規定の中で述べられる要件("strip any leading space characters" や "return false and abort these steps" など)は、そのアルゴリズムの紹介で使われるキーワード("must", "should", "may", など)の意味で解釈するべきです。

いくつかの準拠要件は、属性、メソッド、オブジェクトでの要件として述べられています。このような要件は、ユーザーエージェントでの要件として解釈するべきです。

アルゴリズムや特定の手順として述べられている準拠要件は、最終的な結果が同じ限り、どのような方法で実装しても構いません。(特に、この仕様で定義されるアルゴリズムは見やすさを考慮したもので、パフォーマンスを考慮しているわけではありません。)

この仕様で定義される準拠クラスは、ユーザーエージェントのみです。

ユーザーエージェントは、他の入力に関する実装上の制限を自発的に課しても構いません。例えば、DoS 攻撃を防止するためや、メモリ不足を防ぐためや、プラットフォーム固有の制限に対する回避策などです。

機能のサポートを無効にするとき(セキュリティ問題を低減するための緊急的な手段として、または、開発を補助するため、または、パフォーマンスの理由のため) は、ユーザーエージェントはその機能をまったくサポートしていないかのように、そして、その機能が本仕様には無かったかのように振る舞わなければいけません。たとえば、特定の機能が Web IDL インタフェースにある属性を通してアクセスできるなら、その属性そのものは、そのインタフェースを持ったオブジェクトから除外されます。その属性をオブジェクトに残しておきつつ、null を返したり、例外を投げるようにしただけでは十分ではありません。

2.1 依存関係

この仕様は、いくつかの下層の仕様に依存しています。

HTML

HTML の多くの基本的な概念が本仕様で使われています。 [HTML]

WebIDL

本仕様の IDL ブロックには WebIDL 仕様のセマンティクスを使っています。 [WEBIDL]

3 用語の定義

"Foo オブジェクト" といったら、 Foo は実質的にインタフェースを指すわけですが、"Foo インタフェースを実装したオブジェクト" というより正確な言い方が代わりに使われることがあります。

DOM という用語は、ウェブ・アプリケーションでスクリプトから利用できるようにした API 群を指すために使われますが、必ずしも、DOM Core 仕様で定義されているような Document オブジェクトや他の Node オブジェクトが実際に存在することを意味しているわけではありません。[DOMCORE]

IDL 属性は、その値が(例えば、ウェブ制作者側のスクリプトによって)取り出されているときには、"取得される" と言い、それに新しい値が割り当てられるときには、"セットされる"と言います。

"JavaScript" という用語は、ECMA262 を指すために使われます。公式用語の ECMAScript は使いません。それは、JavaScript という用語の方が広く知られているからです。[ECMA262]

4 API

4.1 Storage インタフェース

interface Storage {
  readonly attribute unsigned long length;
  DOMString key(in unsigned long index);
  getter any getItem(in DOMString key);
  setter creator void setItem(in DOMString key, in any value);
  deleter void removeItem(in DOMString key);
  void clear();
};

それぞれの Storage オブジェクトは、key/value ペアのリストへのアクセスを提供します。key/value ペアのことを項目と呼ぶこともあります。key は文字列です。どんな文字列でも(空文字列を含む)妥当な key です。value は、構造化クローン・アルゴリズムによってサポートされたデータ・タイプなら、何でも構いません。 [HTML]

それぞれの Storage オブジェクトは、生成時に key/value ペアのリストと関連づけられます。これは、sessionStoragelocalStorage 属性のセクションで定義されているとおりです。Storage インタフェースを持ったオブジェクトが別々に複数作られたら、それらはすべて同じ key/value ペアのリストに関連づけられます。

length 属性は、そのオブジェクトと関連づけられたリストにその時点に存在する key/value ペアの数を返さなければいけません。

key(n) メソッドは、リストにある n 番目の key の名前を返さなければいけません。key の順番はユーザーエージェントが定義するものとなりますが、key の数に変更がない限り、オブジェクト内で一貫性を保たなければいけません。(ゆえに、key を追加したり削除したら、key の順番が変わる可能性があります。しかし、実在の key の値を変更しただけなのであれば、そのようなことはありません。)n がオブジェクトにある key/value ペアの数より大きいか等しいなら、このメソッドは null を返さなければいけません。

Storage オブジェクトの名前付きプロパティの名前は、そのオブジェクトと関連づけられているリストにその時点で存在する各 key/value ペアの key となります。

getItem(key) メソッドは、指定の key と関連づけられているその時点の値の構造化クローンを返さなければいけません。指定の key がそのオブジェクトと関連づけられているリストに存在しないなら、このメソッドは null を返さなければいけません。[HTML]

setItem(key, value) メソッドは、指定の value の構造化クローンを最初に生成しなければいけません。ここで例外が発出されたら、その例外が投げられなければいけません。そして、そのオブジェクトと関連づけられているリストは変更されないままでなければいけません。構造化クローンの生成が新規の ImageData オブジェクトの生成を含んでいたなら、代わりに NOT_SUPPORTED_ERR 例外を投げなければいけません。[HTML]

そうでなければ、ユーザーエージェントは、指定の key を持つ key/value ペアが、そのオブジェクトと関連づけられているリストにすでに存在するかどうかをチェックしなければいけません。

もし存在しなければ、新規の key/value ペアがリストに加えられなければいけません。指定の key と、新規に得られた value のクローンにセットされた値を使います。

指定の key がリストに存在していれば、その値は、新規に得られた value のクローンにアップデートされなければいけません。

もし新規の値をセットすることができなかったら、このメソッドは QUOTA_EXCEEDED_ERR 例外を発出しなければいけません。(例えば、ユーザーが該当のサイト用のストレージを無効にしていたり、容量制限オーバーだったなら、その値はセットできなかった可能性があります。)

removeItem(key) メソッドは、指定の key を持つ key/value ペアが存在すれば、それを、そのオブジェクトと関連づけられているリストから削除します。その key を持つ項目が一つもなければ、このメソッドは何もしてはいけません。

setItem()removeItem() メソッドは、失敗に対してはアトミックでなければいけません。失敗した場合においては、このメソッドは何もしません。つまり、データストレージ領域への変更が成功する、または、データストレージ領域が全く変更されない、のいずれかでなければいけません。

clear() メソッドは、そのオブジェクトと関連づけられているリストがあれば、その key/value ペアのすべてを空にします。なければ、このメソッドは何もしてはいけません。

setItem(), removeItem(), clear() メソッドが呼び出されるとき、新たに蓄積される、または、削除されるデータにアクセスすることができる他の Document オブジェクトでイベントが発出されます。sessionStoragelocalStorage 属性のセクションで定義されている通りです。

この仕様では、前述のメソッドに対して、データが物理的にディスクに書き込まれるまで待つことを要求しません。別々のスクリプトから同じ key/value ペアのリストにアクセスしても同じものが見えるという点だけが要求されます。

4.2 sessionStorage 属性

[Supplemental, NoInterfaceObject]
interface WindowSessionStorage {
  readonly attribute Storage sessionStorage;
};
Window implements WindowSessionStorage;

sessionStorage 属性は、トップレベルのブラウジング・コンテキストに限定したストレージ領域のセットを表します。

それぞれのトップレベルのブラウジング・コンテキストは、origin ごとに、固有のセッション・ストレージ領域を持ちます。

ユーザーエージェントは、ブラウジング・コンテキストのセッション・ストレージ領域からデータを失効するべきではありません。しかし、ユーザーが削除を要求したときや、限定されたストレージ領域であることをユーザーエージェントが検知したときや、セキュリティの理由があるときは、そうすることができます。ユーザーエージェントは、データにアクセスすることができるスクリプトが実行されている間は、常に、データの削除を避けるべきです。トップレベルのブラウジング・コンテキストが無くなったとき(それゆえに、永久的にユーザーはアクセスできなくなります。)、セッション・ストレージ領域に蓄積されたデータは、それとともに破棄されることができます。本仕様で説明する API は、それ以降に、そのデータを取り出す手段をまったく提供しません。

ブラウジング・コンテキストの生存期間は、実際のユーザーエージェントのプロセスの生存期間と連動する必要はありません。ユーザーエージェントは、再起動後のセッションのレジュームをサポートすることができます。

トップレベルのブラウジング・コンテキストを持つブラウジング・コンテキストに新規の Document が生成されるとき、ユーザーエージェントは、そのトップレベルのブラウジング・コンテキストが、そのドキュメントの origin 用のセッション・ストレージ領域を持っているかどうかをチェックしなければいけません。もし持っていれば、それがその Document に割り当てられたセッション・ストレージ領域となります。もし持っていなければ、そのドキュメントの origin 用の新規のストレージ領域を生成しなければいけません。そして、それが、その Document に割り当てられるセッション・ストレージ領域になります。Document に割り当てられたストレージ領域は、Document が生存している限り変更されることはありません。たとえ、ネストされたブラウジング・コンテキスト(例:iframe 要素の中)が別の親のブラウジング・コンテキストに移動されたとしてもです。

sessionStorage 属性は、その Document に割り当てられたセッション・ストレージ領域に関連づけられた Storage オブジェクトを返さなければいけません。もしなければ、null を返さなければいけません。それぞれの Document オブジェクトは、その WindowsessionStorage 属性ごとに別々のオブジェクトを持たなければいけません。

新規のトップレベルのブラウジング・コンテキストが既存のブラウジング・コンテキストを複製することで生成されるとき、その新規のブラウジング・コンテキストは、オリジナルと同じセッション・ストレージ領域を使って開始しなければいけません。しかし、この2つのセットは、その時点からは、別々のものとして見なされなければいけません。決してお互いに影響を及ぼしてはいけません。

既存のブラウジング・コンテキストのスクリプトによって、または、既存のブラウジング・コンテキストのリンクをユーザーがたどることによって、または、特定の Document に関係した別の方法で、新規のトップレベルのブラウジング・コンテキストが生成されるとき、その Document の origin のセッション・ストレージ領域は、それが生成されるとき、新規のブラウジング・コンテキストの中にコピーされなければいけません。しかし、その時点から、その 2 つのセッション・ストレージ領域は別々のものとして見なされなければならず、決してお互いに影響を及ぼしてはいけません。

後述の通り、setItem(), removeItem(), clear() メソッドがセッション・ストレージ領域と関連づけられている Storage オブジェクト x 上で呼び出されるとき、もしそのメソッドが何かをしたなら、Window オブジェクトの sessionStorage 属性の Storage オブジェクトが同じストレージ領域と関連づけられているすべての Document オブジェクトの中で、x を除いて、storage イベントが発出されなければいけません。

4.3 localStorage 属性

[Supplemental, NoInterfaceObject]
interface WindowLocalStorage {
  readonly attribute Storage localStorage;
};
Window implements WindowLocalStorage;

localStorage オブジェクトは、origin 用の Storage オブジェクトを提供します。

ユーザーエージェントは、それぞれの origin ごとに、ローカル・ストレージ領域のセットをひとつずつ持たなければいけません。

ユーザーエージェントは、セキュリティの理由がある場合、または、ユーザーからのリクエストがあるときに限り、ローカル・ストレージ領域からデータを失効するべきです。ユーザーエージェントは、そのデータにアクセスすることができるスクリプトが実行している間は、常に、データの削除を避けるべきです。

localStorage 属性にアクセスされたら、ユーザーエージェントは次の手順を実行しなければいけません:

  1. ユーザーエージェントは、リクエストがポリシーに違反するなら、Storage オブジェクトを返すのではなく、SECURITY_ERR を投げることができます(例:ユーザーエージェントが、そのページにデータを残せないよう設定されている場合)。

  2. Document の origin が scheme/host/port の構成でないなら、SECURITY_ERR 例外を投げ、これらの手順を中止します。

  3. この属性がアクセスされた Window オブジェクトの Document の origin に対するローカル・ストレージ領域をユーザーエージェントが割り当てたかどうかをチェックします。割り当てていないなら、その origin に対するストレージ領域を新規に生成します。

  4. その origin のローカル・ストレージ領域に関連づけた Storage オブジェクトを返します。それぞれの Document オブジェクトは、その WindowlocalStorage 属性に対して、別々にオブジェクトを持たなければいけません。

後述の通り、setItem(), removeItem(), clear() メソッドがセッション・ストレージ領域と関連づけられている Storage オブジェクト x 上で呼び出されるとき、もしそのメソッドが何かをしたなら、Window オブジェクトの localStorage 属性の Storage オブジェクトが同じストレージ領域と関連づけられているすべての Document オブジェクトの中で、x を除いて、storage イベントが発出されなければいけません。

localStorage 属性の Storage オブジェクトのプロパティが調べられるとき、返されるとき、セットされるとき、削除されるときは常に、直接的なプロパティ・アクセスだったかどうかにかかわらず、プロパティ列挙の間にプロパティの存在をチェックするとき、存在しているプロパティの数を調べるとき、または、Storage インタフェースに定義されたメソッドやプロパティが実行されるとき、ユーザーエージェントは、まず最初に、ストレージのミューテックスを取得しなければいけません。

4.3.1 セキュリティー

ユーザーエージェントは、localStorage 属性によって返された Storage オブジェクトのメンバーのうち一つでも、有効なスクリプトの origin と、localStorage 属性がアクセスされた Window オブジェクトの Document の origin と同じでないときは常に、SECURITY_ERR 例外を投げなければいけません。

これは、document.domain 属性が使われるときには Storage オブジェクトは中性化されることを意味します。

4.4 storage イベント

storage イベントは、前の 2 つのセクション(セッション・ストレージローカル・ストレージ)で説明した通り、ストレージ領域が変化したときに発出されます。

これが起こるとき、ユーザーエージェントは、影響の対象となる Storage オブジェクトを持つ Document オブジェクトの Window オブジェクトそれぞれで、storage という名前のイベントを発出するタスクをキューイングしなければいけません。このイベントは、名前空間を持たず、バブリングせず、キャンセルすることはできません。そして、StorageEvent インタフェースを使います。

これは、完全にアクティブでない Document オブジェクトを含みます。しかし、それらの上で発出されたイベントは、その Document オブジェクトが再び完全にアクティブになるまで、イベント・ループによって無視されます。

このタスクに対するタスク・ソースは、DOM 操作タスク・ソースです。

このイベントが setItem()removeItem() メソッドの起動によって発出されているなら、このイベントによって、その key 属性 には、該当のキーの名前がセットされなければいけません。そして、その oldValue 属性には、該当のキーの古い値の構造化クローンがセットされなければいけません。ただし、新たに追加された場合は null となります。さらに、その newValue 属性には、該当のキーの新たな値の構造化クローンがセットさればいけません。ただし、キーが削除された場合は null となります。[HTML]

そうでなければ、このイベントが clear() メソッドの起動によって発出されているなら、このイベントによって、その key, oldValue, newValue 属性には null がセットされなければいけません。

さらに、このイベントによって、その url 属性には、影響を受けた Storage オブジェクトのドキュメントのアドレスがセットされなければいけません。そして、このイベントによって、その storageArea 属性には、対象の DocumentWindow オブジェクトから得られた Storage オブジェクトがセットされなければいけません。そのオブジェクトは、影響を受けた時点における同じ種類の Storage 領域(つまり、セッションまたはローカル)を表すものです。

4.4.1 イベント定義

interface StorageEvent : Event {
  readonly attribute DOMString key;
  readonly attribute any oldValue;
  readonly attribute any newValue;
  readonly attribute DOMString url;
  readonly attribute Storage storageArea;
  void initStorageEvent(in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in DOMString keyArg, in any oldValueArg, in any newValueArg, in DOMString urlArg, in Storage storageAreaArg);
};

initStorageEvent() メソッドは、DOM Events インタフェースの類似の名前のメソッドと同じように、イベントを初期化しなければいけません。[DOMEVENTS]

key 属性は、変更されたキーを表します。

oldValue 属性は、変更されたキーの古い値を表します。

newValue 属性は、変更されたキーの新しい値を表します。

url 属性は、キーが変更されたドキュメントのアドレスを表します。

storageArea 属性は、影響を受けた Storage オブジェクトを表します。

4.5 スレッド

ストレージの排他制御の採用によって、複数のブラウジング・コンテキストから同時にローカルストレージ領域にアクセスすることができるでしょう。このとき、スクリプトは、並列にスクリプトが実行されていることにまったく気づくことはできません。

ゆえに、Storage オブジェクトの length 属性と、そのオブジェクトの各種プロパティの値は、スクリプト自身によって予測可能な方法を除いて、スクリプト実行中に変更することができません。

5 ディスク・スペース

ユーザーエージェントは、ストレージ領域として利用できるスペースの総量を制限するべきです。

ユーザーエージェントは、サイトが origin のもとで他のアフィリエイト・サイトのデータを蓄積することを防ぐべきです。例えば、a1.example.com, a2.example.com, a3.example.com などで上限まで蓄積し、メインとなる example.com のストレージ制限をうまくくぐり抜けるといったことです。

ユーザーエージェントは、クォータ制限に達したときユーザーに通知して、ユーザーがサイトに追加のスペースを許可できるようにすることができます。これによって、例えば、サイトは、ユーザーのコンピュータ上に、多くのユーザー生成ドキュメントを蓄積できるようになります。

ユーザーエージェントは、各ドメインがどれくらいスペースを使っているのかをユーザーが見ることができるようにするべきです。

ほとんど任意ではありますが、origin ごとに 5 MB の任意リミットが推奨されます。実装のフィードバックは歓迎です。将来的に、この案のアップデートのために使われるかもしれません。

6 プライバシー

6.1 ユーザー追跡

サードパーティの広告主(または、複数のサイトにコンテンツを配布することができるもの)は、複数のセッションをまたいでユーザーを追跡するために、そのローカル・ストレージ領域にユニークな識別子を蓄積することが考えられます。精度の高いターゲッティング広告を可能にするために、ユーザー興味のプロファイルを構築するのです。ユーザーの実際の身元を知っているサイト(例えば、身分証明書を要求する e コマースサイト)と連動すれば、これは、不当なグループに、純粋に匿名のウェブ利用を前提とした世界と比べて、より高い精度で個人を標的にすることを許してしまうことになります。

ユーザー追跡のリスクの軽減に使えるのテクニックが数多くあります:

サードパーティのストレージをブロックする

ユーザーエージェントは、ブラウジング・コンテキストのトップレベルのドキュメントのドメインのスクリプトに対して、localStorage オブジェクトへのアクセスを制限することができます。例えば、iframe の中のページが別ドメインなら、その API へのアクセスを拒否するのです。

蓄積データを失効する

ユーザーエージェントは、設定に応じて、ある期間の経過後に蓄積データを自動的に削除することができます。

例えば、ユーザーエージェントは、サードパーティのローカル・ストレージ領域を、セッション専用ストレージとして扱うことができるでしょう。ユーザーが、それにアクセスできるブラウジング・コンテキストをすべて閉じたら、削除するのです。

これによって、ユーザーを追跡するサイトの能力を制限することができます。そのサイトは、ユーザーがそのサイトで認証(購入したり、サービスにログインする)しない限り、複数のセッションをまたいで、そのユーザーを追跡することができないからです。

しかし、これは、長期間に渡って保存するストレージ・メカニズムとしての API の利便性を下げることにもなります。また、ユーザーがデータ失効の実装を完全に理解していないと、ユーザーのデータをリスクにさらすことにもなります。

永続的なストレージをクッキーとして扱う

ユーザーが自分のプライバシーを守るために、クッキーだけをクリアしたとしても、ローカル・ストレージ領域に蓄積されたデータをクリアしなければ、サイトは二つの機能を互いのバックアップとして使うことで、そのユーザーの試みを台無しにすることができてしまいます。ユーザーエージェントは、この可能性をユーザーに理解してもらうよう配慮し、すべての永続的なストレージ機能にあるデータを同時に削除できるようにするといった方法で、これらをクリアするためのインタフェースを提供するべきです。 [COOKIES]

ローカル・ストレージ領域にアクセスできるサイトを個別に指定ができるホワイト・リスト

ユーザーエージェントは、特に制限を設けずにサイトがセッション・ストレージにアクセスできるようにすることができます。しかし、ローカル・ストレージ領域にはユーザーにアクセス権を求めることができます。

蓄積データの origin 追跡

ユーザーエージェントは、データ蓄積を発生させたサードパーティの origin のコンテンツを含んだサイトの origin を記録することができます。

この情報が永続的ストレージにあるデータを見せるために使われるなら、それは、ユーザーが永続的ストレージのどの部分を取り除けばよいかを判断する材料になるでしょう。ブラックリスト("このデータを削除し、二度とこのドメインがデータ蓄積できないようにします")と組み合わせれば、そのユーザーは、自分が信頼するサイトだけに永続的ストレージが利用できるよう制限することができます。

共有ブラックリスト

ユーザーエージェントは、ユーザーが永続的ストレージのドメインのブラックリストを共有できるようにすることができます。

こうすることで、コミュニティーにおいて、協力し合いながらプライバシーを守る活動ができるようになるでしょう。

これらの提案によって、ユーザー追跡のためにこの API をある程度は利用できないようになりますが、完全にブロックするわけではありません。単一ドメイン内では、サイトはセッションが続く間、ユーザーを追跡し続けることができます。そして、サードパーティにこの情報を、何かしらの識別情報(名前、クレジットカード番号、住所)とともに、引き渡すことができます。もし、そのような情報を取得するために、サードパーティが複数のサイトと協力しあっているなら、プロファイルを生成することができてしまいます。

しかし、ユーザー追跡は、ユーザーエージェントの助けがまったく無くても、ある程度は可能となるのです。例えば、URL にセッション識別子を入れたり、害のない目的で一般的に使われているテクニックを、ユーザー追跡用に目的を変えて使うのです(過去にさかのぼってすら)。この情報を他のサイトと共有することができるのです。訪問者の IP アドレスや、そのほかユーザー固有データ(例えば、ユーザーエージェントのヘッダーや設定情報)を使って、別々のセッションを統合して、一貫したユーザー・プロファイルに仕立てるのです。

6.2 データの機密性

ユーザーエージェントは、永続的に蓄積したデータを、機密性が高くセンシティブである可能性があるとして扱うべきです。それは、e-mail かもしれないし、予約のスケジュールや健康管理記録かもしれません。このメカニズムに蓄積されるのは、こういった極秘文書かもしれないのです。

そのため、ユーザーエージェントは、データを削除するとき、確実に該当のストレージから速やかに削除されるようにするべきです。

7 セキュリティー

7.1 DNS スプーフィング・アタック

DNS スプーフィング・アタックの可能性のため、ある特定のドメインにあると宣言しているホストが、本当にそのドメインからのものなのかを、誰も保証することはできません。これを低減するために、ページに TLS を使うことができます。TLS を使ったページであれば、確実に、同じドメインであることを確認できる証明書を持つ TLS を使ったページだけに、そのストレージ領域にアクセスできるようにすることができます。

7.2 クロス・ディレクトリ・アタック

ひとつのホスト名を別々のウェブ制作者で共有する場合、例えば、geocities.com にコンテンツをホスティングするユーザーは、全員、ひとつの永続ストレージオブジェクトを共有することになります。パス名でアクセスを制限する機能はありません。そのため、共有ホストのウェブ制作者は、これらの機能を使わないことが推奨されます。他のウェブ制作者にデータを読まれたり上書きされたりしてしまうかもしれないからです。

たとえ、パス制限機能が使えるようにしたとしても、DOM スクリプティング・セキュリティ・モデルでは、その防御をバイパスでき、どんなパスのデータでもアクセスできてしまいます。

7.3 実装のリスク

これらの永続ストレージ機能実装時には、主に 2 つのリスクが伴います。ひとつは、危険なサイトに別のドメインの情報を読まれてしまうこと、もう一つは、危険なサイトに、別のドメインから読まれた情報を書き換えられてしまうことです。

サードパーティのサイトに、彼らのドメインから読まれることになっていないデータを読まれてしまうというのは、情報漏洩を引き起こすことになります。例えば、あるドメインのユーザーのショッピング・ウィッシュリストが、ターゲッティング広告のために、別のドメインによって使われてしまうことが考えられます。また、文書作成サイトによって蓄積された制作中の機密文書を、競合の会社のサイトによって詮索されるかもしれません。

サードパーティのサイトによって別のドメインの永続ストレージにデータを書き込まれると、情報なりすましという結果となる可能性があります。例えば、危険なサイトがユーザーのウィッシュリストに商品を追加することが考えられます。また、危険なサイトが、餌食となるサイト上で、ユーザーのセッション識別子を、ユーザーのアクションを追跡可能な既知の ID にセットしてしまうことが考えられます。

そのため、本仕様で説明されている origin モデルに厳密に従うことが、ユーザーのセキュリティ上、重要なのです。

リファレンス

"非規定" とマークされていない限り、すべてのリファレンスが規定です。

[COOKIES]
HTTP State Management Mechanism, A. Barth. IETF.
[DOMCORE]
Document Object Model (DOM) Level 3 Core Specification, A. Le Hors, P. Le Hegaret, L. Wood, G. Nicol, J. Robie, M. Champion, S. Byrnes. W3C.
[DOMEVENTS]
Document Object Model (DOM) Level 3 Events Specification, D. Schepers. W3C.
[ECMA262]
ECMAScript Language Specification. ECMA.
[HTML]
HTML, I. Hickson. WHATWG.
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels, S. Bradner. IETF.
[WEBIDL]
Web IDL, C. McCormack. W3C.

謝辞

謝辞の完全なリストは、HTML 仕様をご覧ください。 [HTML]