localStorageとpostMessage
postMessageを使うことで、サーバを経由しないで異なるドメイン間でのデータ受け渡しが可能だということを説明しました。またlocalStorageを使うことで「サーバには送られないがブラウザにのみ保持されるデータ」を気軽に作ることができるようになりました。
「他サービスのid」ならまだしも「どんなサービスを使っているのか」までも秘匿(ひとく)したいケースは実際にはまれでしょうから、そこまでストイックな、完全にクライアントサイドでのみデータを保持するサービスにこだわる必要もないでしょうが、方向性としては興味深いと思います。クライアントサイドでのみデータを保持するということは、ブラウザのキャッシュをクリアしたらデータが消えるということでもあります。そんな状況は好ましくないでしょうから、localStorageに本当に永続化が必要な情報を入れてしまうのは避けるほうが賢明です。そのため、localStorageは、キャッシュ用のストレージとして使ってサーバへのアクセスを減らす用途、あるいは、消えても再度生成することができるデータを格納する用途に使うのが適しています。外部サービスのOAuthアクセストークンは、利便性のために一定期間保持しておきたいが、もし消えてしまっても、再度認証することでいつでも再生成することができます。サーバを経由せずにブラウザのみで外部サービスと連携するような機能を作りたい場合には、localStorageは重宝することになるでしょう。
秘密情報の保存先としてのlocalStorage
特殊なケースでは、クライアントサイドでの暗号化に利用するケースが挙げられます。クライアントサイドでの暗号化/復号は、パスワード管理サービスなど「ユーザの保存しているデータがサービス提供者からも参照できてはいけない」という場合に役に立ちます。
こういったサービスには主に2種類の傾向があります。
① サーバに送信されないパスフレーズ[9] を追加で設定するもの
② ユーザのパスワードから異なるハッシュ関数を使い2種類のハッシュ値を生成して、片方をサーバに対するログインの用途で使用し、もう片方はサーバに送信せずにローカルでの暗号化/復号に使うためのもの
この2種類です。サーバ側にはそもそも生のパスワードが送信されていないため、Webページ上のJavaScriptに暗号化キーを盗みだすようなコードが含まれていなければ、サーバ側では復号した状態のテキストを読み取れないということが保証できます。
こういったサービスを作るためには、ログイン後にもJavaScriptの変数として、暗号化/復号のためのパスフレーズを保持したままにする必要があります。localStorageが使えない場合、頻繁にパスフレーズを問い合わせることになるか、ログイン後にも保持されるフレームを作っておくか、あるいはフルAjax(Asynchronous JavaScript and XML )でログイン後にも画面遷移をしない、といった実装をする必要がありました。
しかしサーバに勝手に送信されることはないとは言っても、localStorageはパスワードのような機密性の高い情報を格納することを目的として設計されているわけではありませんので、注意が必要です[10] 。
サーバに送信されてしまうlocalStorageのデータ
本稿で少し触れましたが、localStorageで保存されたデータや、postMessageで送受信されるメッセージがサーバに送られないというのは、あくまで「サーバに送信するようなJavaScriptコードが含まれていない」場合のみです。
外部サービスのパスワードの入力を求めるサービスに、たとえば「パスワードはサーバには保存されません」といった語彙を使われていると、localStorageにのみパスワードを保持してサーバに送信すらしない ものなのか、サーバに送信して一時的に使うが永続的に保存はしていない 、という意味なのか、区別があいまいになります。どちらを一般的な挙動だと考えるかは、時代によって変わっていくものでしょう。
たとえば、メールソフトを作っている会社が、ソフトウェアの中でメールアカウントのパスワードを求めていたとしても、そのパスワードはあくまで使っているPC上に保存されるもので、メールソフトを作ってる会社に対して入力しているとは考えないでしょう(注a ) 。
しかし一方で、Webメールサービスが、外部のメールを取り込むために、ほかのメールアカウントのパスワードの入力を求めているなら、それはサーバ側でパスワードを預かっている、ということになります。具体的には、Thunderbirdにメールアカウントのパスワードを入れても、そのパスワードがMozillaに送られるとは考えないでしょう。一方Gmailに外部メールのパスワードを入れれば、それはGoogleのサーバに保存されるだろう、と考えるでしょう。ブラウザから直接POP3やIMAP(Internet Message Access Protocol )を使ってメールを受信するのは難しそうですし(注b ) 、Gmailを表示していない間もバックグラウンドでメールを受信して取り込む必要があります。
特にスマートフォン上で動くアプリケーションは、こういった境界線があいまいになっています。たとえば、TwitterやFacebookと連携する機能があるスマートフォン向けのアプリケーションがあった場合、アプリケーションから直接TwitterやFacebookのAPIにアクセスしているのか、アプリケーション提供者のサーバを経由してTwitterやFacebookのAPIにアクセスしているのか、容易には区別がつきません。スマートフォン向けのアプリケーションの中には、プッシュ通知機能やサービス間での連携機能のために、付随するサーバサイドのサービスに対してもアクセス権限を受け渡ししていることがあります。デスクトップアプリケーションの延長としてとらえた場合「そのパスワード/アクセストークンはローカルにのみ保持されるものだろう」と考えるでしょう。
Webアプリケーションの延長として考えた場合は、当然、その設定情報はサーバにも保存されるものだ、と考えることでしょう。
多くのアプリケーションが実際には内部的にHTMLとJavaScriptを使って構築された「特定のWebサイトの利用に特化して最適化されたフロントエンド」という状況で、どちらが当たり前であるのか、現時点では断言できません。
認証サービスへの応用
localStorageとpostMessageが使える前提であれば、シングルサインオンは非常にシンプルに再実装できます。ここではMozilla persona(旧BrowserID)から見る、localStorageとpostMessageの使い道について解説します。
Mozillaは将来的にブラウザでのネイティブサポートを計画していますが、https://login.persona.org/
においてJavaScript、localStorage、postMessageを使った実装を見ることができます。ログインボタンを押すと、ポップアップウィンドウでlogin.persona.org
が開きます。login.persona.org
はブラウザで秘密鍵/公開鍵のペアを作成し、秘密鍵はlocalStorage内にのみ保持します。login.persona.orgは、呼び出し元の親ウィンドウのドメインに対してメールアドレスを通知して良いかユーザに確認を求めたあと、postMessageで署名されたメッセージを送ります。親ウィンドウは送られてきたメッセージを検証することで、メールアドレスを取り出すことができます。
クライアント側がやることはいたってシンプルです。Webページ側はJavaScriptのコードを数行追加するだけ。サーバ側では、メッセージに含まれるアサーションを受け取って、idの発行元に対して検証するためのHTTPリクエストを投げるだけです。
「自分のメールアドレス『自称』who@example.com
です」と単なる入力補助目的であれば、メールアドレスの持ち主かどうかを証明する必要がありません。単に宛先となるドメインを確認したうえで「自称」のメールアドレスをpostMessageで送ればよいということになります。「 そのメールアドレスが到達する人間です」と証明して、認証に利用するためには、login.persona.org
が登録されたメールアドレスに対してメールを送信し、そのメールの持ち主であることを確認しておきます。
ブラウザによるネイティブサポートか、あるいはJavaScriptの利用が必須となってしまうため「シングルサインオンのしくみ」として普及するかどうかは未知数ですが、少なくとも「到達性が確認されたメールアドレスの入力を求める」といった用途では、こういったWebサイト側の実装の負担が少ない認証方式が何らかの形で普及していくのではないかと思います。
まとめ
WebSocketのようなリアルタイムの双方向通信であったり、派手なグラフィックスや位置情報、ハードウェアへのアクセスなど、近年におけるブラウザの進歩は目覚ましいものがあります。これらのHTML5に含まれる新しいAPIは、Webサイトというよりは、ゲームを作るために必要なものだったり、ブラウザに限らずHTML5をベースとしたアプリケーションプラットフォームを魅力的なものにするために、節操なく新しい仕様が増えていっているという状況であるとも言えます。今までネイティブアプリケーションでなければ不可能だったことが、これからはどんどんブラウザ上でも実現可能になるでしょう。
それに比べるとpostMessageとlocalStorageは地味ですし、華々しさはありませんが、重要なAPIだと筆者は考えています。それは、Webサイト間の連携を実現するうえで、必要不可欠な存在だと感じるからです。
postMessageは今まで混沌としていた、クロスドメインの通信手法を統一し、より安全に使えるものにしました。localStorageは、暗号化のための秘密鍵や、ほかのサービスに対するアクセストークンなどを「ブラウザにのみ」保存できます。ブラウザが単なる「アプリケーション」プラットフォームではなく、Webならではの魅力を備えた「Webアプリケーション」プラットフォームとして機能するために、postMessageやlocalStorageは重要な役割を担っていると言えるでしょう。