フロントエンドWeb戦略室

第3回localStorageとpostMessageの使いどころ(3)

localStorageとpostMessage

postMessageを使うことで、サーバを経由しないで異なるドメイン間でのデータ受け渡しが可能だということを説明しました。またlocalStorageを使うことで「サーバには送られないがブラウザにのみ保持されるデータ」を気軽に作ることができるようになりました。

「他サービスのid」ならまだしも「どんなサービスを使っているのか」までも秘匿(ひとく)したいケースは実際にはまれでしょうから、そこまでストイックな、完全にクライアントサイドでのみデータを保持するサービスにこだわる必要もないでしょうが、方向性としては興味深いと思います。クライアントサイドでのみデータを保持するということは、ブラウザのキャッシュをクリアしたらデータが消えるということでもあります。そんな状況は好ましくないでしょうから、localStorageに本当に永続化が必要な情報を入れてしまうのは避けるほうが賢明です。そのため、localStorageは、キャッシュ用のストレージとして使ってサーバへのアクセスを減らす用途、あるいは、消えても再度生成することができるデータを格納する用途に使うのが適しています。外部サービスのOAuthアクセストークンは、利便性のために一定期間保持しておきたいが、もし消えてしまっても、再度認証することでいつでも再生成することができます。サーバを経由せずにブラウザのみで外部サービスと連携するような機能を作りたい場合には、localStorageは重宝することになるでしょう。

秘密情報の保存先としてのlocalStorage

特殊なケースでは、クライアントサイドでの暗号化に利用するケースが挙げられます。クライアントサイドでの暗号化/復号は、パスワード管理サービスなど「ユーザの保存しているデータがサービス提供者からも参照できてはいけない」という場合に役に立ちます。

こういったサービスには主に2種類の傾向があります。

  • ① サーバに送信されないパスフレーズ[9]を追加で設定するもの
  • ② ユーザのパスワードから異なるハッシュ関数を使い2種類のハッシュ値を生成して、片方をサーバに対するログインの用途で使用し、もう片方はサーバに送信せずにローカルでの暗号化/復号に使うためのもの

この2種類です。サーバ側にはそもそも生のパスワードが送信されていないため、Webページ上のJavaScriptに暗号化キーを盗みだすようなコードが含まれていなければ、サーバ側では復号した状態のテキストを読み取れないということが保証できます。

こういったサービスを作るためには、ログイン後にもJavaScriptの変数として、暗号化/復号のためのパスフレーズを保持したままにする必要があります。localStorageが使えない場合、頻繁にパスフレーズを問い合わせることになるか、ログイン後にも保持されるフレームを作っておくか、あるいはフルAjaxAsynchronous JavaScript and XMLでログイン後にも画面遷移をしない、といった実装をする必要がありました。

しかしサーバに勝手に送信されることはないとは言っても、localStorageはパスワードのような機密性の高い情報を格納することを目的として設計されているわけではありませんので、注意が必要です[10]⁠。

認証サービスへの応用

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は重要な役割を担っていると言えるでしょう。

おすすめ記事

記事・ニュース一覧