これはFirefox OS Advent Calendar 2013 12/14の記事です。
こんにちは、にしむねあです。 この記事ではGeckoのlocalStorageの実装を紹介します。 私はセキュリティの分野に興味があるので、データのアクセス制御がどのように実装されているかを掘り下げてみたいと思います。
localStorageはウェブのクライアントに永続的なデータを保存する機能です。 W3CのWeb Storageで仕様が定義されており、広義の解釈ではHTML5に含まれます。
これまでにもクライアントにデータを保存する仕組みとしてはCookieが使われてきましたが、 保存できるデータの上限が4kB程度であるため利用用途は限られていました。 localStorageの場合は標準的なブラウザであれば2MB以上のデータが保存できますので、 サーバ上のコンテンツのキャッシュなど幅広い用途に利用することができます。
localStorageには同一生成元ポリシーに基づくアクセス制御が適用されます。 このため、データはオリジン(スキーム+ホスト名+ポート番号の組)ごとに分離され、 異なるオリジンのデータにアクセスすることはできません。
それでは、GeckoがlocalStorageをどのように実装しているかを見ていきましょう。
localStorageのデータはprofileディレクトリの直下にあるwebappsstore.sqliteというファイルに保存されます。 それぞれのオリジンのデータはファイル単位では分離されておらず、まとめて一つのSQLiteファイルに記録されています。
-
Mac版Firefoxブラウザの場合
- ~/Library/Application Support/Firefox/Profiles/{profile}/webappsstore.sqlite
-
Firefox OSの場合
- /data/b2g/mozilla/{profile}/webappsstore.sqlite
webappsstore.sqliteにはwebappsstore2というテーブルがあり、全てのデータはこの中にレコードとして記録されます。 レコードの内容はFirefoxブラウザとFirefox OSで異なりますので、個別に紹介していきます。
例えば http://example.com/ というサイトでfoo=barというデータがlocalStorageに保存された場合、 webappsstore2テーブルには以下のようなレコードが追加されます。
scope | key | value | secure | owner |
---|---|---|---|---|
moc.elpmaxe.:http:80 | foo | bar |
ここでscopeの「moc.elpmaxe.:http:80」という文字列は以下の値を「:(コロン)」で連結したものです。
値 | 意味 |
---|---|
moc.elpmaxe | ホストのFQDNを逆さにしたもの (example.comの逆) |
http | ホストのスキーム |
80 | ホストのポート番号 |
つまり、scopeはデータのオリジンを表しています。
Firefox OSでは端末にインストールされたアプリもlocalStorageを使用することができます。 各アプリのデータはWebアプリのサンドボックスで保護されており、異なるアプリのlocalStorageにアクセスすることはできません。また、アプリが<iframe>や<iframe mozbrowser>によってアプリ以外のページを表示した場合はブラウザAPIのサンドボックスが適用されるため、アプリがiframe内部のlocalStorageにアクセスすることや、その逆を行うことはできません。
Firefox OSではこのようなサンドボックスを実装するため、 webappsstore2テーブルのscopeの値にも幾つかの要素が追加されています。
値 | 意味 |
---|---|
1005 | アプリID |
f | browserContentフラグ (ブール値) |
moc.elpmaxe | データを所有するオリジンのFQDNを逆さにしたもの |
http | データを所有するオリジンのスキーム |
80 | データを所有するオリジンのポート番号 |
Firefox OSのアプリにはインストール時にアプリID(appID)と app:// で始まるオリジンが割り当てられます。 このアプリIDとオリジンを用いてサンドボックスによるデータの分離が実現されています。
例えば以下のようなアプリを考えてみましょう。
- アプリにはアプリID=1005番、_app://hoge.org_というオリジンが割り当てられている
- アプリのコードがlocalStorageにデータを保存する
- 次のような二つのiframeを用いて http://example.com/ という外部のページを表示する
<iframe src="http://example.com/">
<iframe src="http://example.com/" mozbrowser>
- http://example.com/ のコードもlocalStorageにデータを保存する
この場合、それぞれのlocalStorageのデータは次のように異なるscopeとなります。
- アプリのコードが保存したデータ
- 1005:f:org.hoge.:app
- <iframe src="http://example.com/"> 内で保存されたデータ
- 1005:f:moc.elpmaxe.:http:80
- <iframe src="http://example.com/" mozbrowser> 内で保存されたデータ
- 1005:t:moc.elpmaxe.:http:80
GeckoにはlocalStorageのデータに対するアクセス制御とは別に、 localStorage自体へのアクセスを制御する仕組みが実装されています。 この仕組みを用いることにより、Gecko全体、あるいはドメインやページ単位でlocalStorageを無効化することができるようになります。 どのような仕組みがあるかを簡単に紹介します。
GeckoにはPreferenceという環境変数があり、dom.storage.enabledはその中の1つです。 Firefoxブラウザの場合はコンフィグ画面(about:config)でPreferenceの値を閲覧、変更することができます。
dom.storage.enabledはWeb Storage機能の有効/無効を切り替える設定値です。 これをfalse(無効)にするとGecko全体でlocalStorageが使用できなくなります。
HTML5では<iframe>にsandboxという属性が追加され、これによりiframe内で表示したページの機能を制限できるようになりました。 sandbox属性にはallow-same-originという制限を緩和するためのトークンが指定でき、 これを「指定していない」場合はiframe内部のページが独自のオリジンを持ちます。 このためiframe内のページが例え親フレームと同じオリジンであったとしても親フレームのDOM要素にはアクセスできなくなります。
Geckoではiframeにsandbox属性が指定されており、かつ、allow-same-originが指定されていない場合は、 iframe内部のページからlocalStorageを使用することが禁止されます。
Firefoxブラウザにはサイト別設定マネージャー(about:permissions)という機能があり、ドメインごとにプラグインの実行や位置情報をON/OFFすることができます。この中には「Cookieデータの保存」という設定項目があり「許可」「拒否」「セッション中のみ許可」という3つの設定値が選択できるようになっています。
「Cookieデータの保存」はCookieだけでなくlocalStorageの保存にも影響を与えます。 具体的にはこの設定が「拒否」の場合は、そのドメインに属するページからのlocalStorageのアクセスが拒否され、「セッション中のみ許可」の場合はlocalStorageが呼び出された場合でも代わりにsessionStorageが使用されるようになります。
プライベートブラウジングモードとは閲覧したページの履歴を一切残さないようにする機能です。 プライベートブラウジングモードが有効の場合は、localStorageのデータもウィンドウが閉じたときに破棄されなければなりません。 このためGeckoの内部ではlocalStorageを呼び出した場合でも内部的にsessionStorageが呼び出されるようになっています。
network.cookie.cookieBehaviorはPreferenceの1つで、ブラウザがどのようにCookieを取り扱うかを指定する設定値です。 初期値は0(全てのcookieを許可)ですが、2(全てのCookieを使用禁止)が指定されている場合はCookieだけでなくlocal Storageの使用も禁止となります。
network.cookie.lifetimepolicyもPreferenceの1つで、Cookieの保存期間を指定するための設定値です。 初期値は0(サーバの指定した期間に準ずる)ですが、1(毎回ユーザーに確認する)が指定されている場合はlocal Storageの使用が禁止され、2(ブラウザ終了時まで保存)が指定されている場合はlocalStorageの代わりにsession Storageが呼び出されるようになります。
この記事ではGeckoのlocalStorageの実装を紹介しました。 Geckoには様々な機能があるためlocalStorageのアクセス制御を見ても様々なアクセス制御の仕組みが含まれていることが分かります。 このような発見があるのはブラウザのコードを読む醍醐味ですね。
さて、2013年もあと僅かとなりました。 今年はFirefox OSやGeckoの勉強会を通じてたくさんの方と出会うことができ、とても充実した一年になりました。 来年はもっとコミュニティを盛り上げていくお手伝いができるように頑張りますので、 今後とも宜しくお願い致します。
さいごのさいごに宣伝です。
TechBoosterさんから出版されるFirefox OSの同人誌「狐物語 -かえんフォックス-」にて OSのアーキテクチャに関する記事を担当させて頂きました。 年末のコミケでの発売となります。
表紙もこのように非常にチャーミングな仕上がりとなっておりますので、 コミケに行く予定のある方は是非、TechBoosterさんのブース「C85」にお立ち寄りください!
テクブさんのブースは「3日目 西地区す-24a」ですよー。