パーソナルデータのmashupとクロスドメインJSONのセキュリティ(3)

これまでにリファラ情報を利用したJSONサービスのセキュリティ実装について提案してみた。ただし、これはあくまでセキュリティの実現手段の話であり、当たり前であるが設定するセキュリティポリシーによって挙動は異なるべきものである。そこで今回は、このセキュリティポリシーをどのように実装するかについて考えたいと思う。

ユーザがサーバに預けているデータは、異論はあるかもしれないが、一部の例外を除いて基本的にユーザの自由になるべきものである。たとえば、スケジュールであれ住所録であれ、サーバに預けておいたデータはいつでもユーザの自由なときに持ち出して利用したい。もちろん、サービス側にもデータを保持するコストがかかるわけで、できれば預かっているデータは自分のサービス専用のデータとして保持しておきたい場合がほとんどだろう。その気持ちはよくわかる。しかし、少し視点を変えて、ユーザの自由度を増やすことでサービス自体の魅力が増す可能についても考えてみてはどうか。個人情報保護の観点で言っても、データをユーザ中心に制御を戻せば、サービス間でのデータ共有にまつわる大半の問題は回避できる*1


とりあえず、前回の例と同じようにオンラインCRMサービスを例に挙げる。ここでは、CRMサービスに保管されている顧客コンタクトリストを、ユーザの同意の元に任意のサーバに送信できるような仕組みについて考える。



まず、コンタクトリストデータを利用したい 3rd Partyサービス(www.3rdparty.com)は、オンラインCRMサービスのJSONサービスエンドポイントに対してリクエストを送信する。この際、実際にHTTPリクエストを送信するのはユーザのブラウザであることに注意。

http://www.examplecrmservice.com/json/contactlist?callback=handleContactList

しかし、現在 www.3rdparty.com はセキュリティポリシーにより現在顧客リストを提供できるようになってはいない。そのため、例えば以下のようなレスポンスを返す。

handleContactList({error : "NOT_AUTHORIZED"});

呼び出し側のアプリケーションでは、コンタクトリストが得られた場合とは別にハンドルできるようにコールバック関数を定義しておく。

function handleContactList(response) {
  if (response.error && response.error=='NOT_AUTHORIZED') {
    authSubRequest();
  } else { 
    renderContactList(response); // コンタクトリストを描画
  }
}

上記のように定義した場合、authSubRequest関数では、あらかじめ与えられているJSONサービスのサブスクリプションURL(http://www.examplecrmservice.com/authSubReq)に対してリダイレクトする。

この際に引数として

  1. resource : JSONサービスを表す一意のリソース名
  2. application : アプリケーションを表す一意のリソース名(e.g. URL)
  3. returnUrl : リダイレクトからの戻りURL

を渡している

function authSubRequest() {
  var authSubReqUrl = 'http://www.examplecrmservice.com/authSubReq';
  authSubReqUrl += '?resource=contactlist';
  authSubReqUrl += '&application='+escape('http://www.3rdparty.com');
  authSubReqUrl += '&returnUrl='+escape(location.href);
  location.href = authSubReqUrl;
}

このリダイレクトの結果、制御はオンラインCRMサービス(www.examplecrmservice.com)に移ることになる。オンラインCRMサービス側では、リクエストを送信しているユーザを識別した上で、以下のようなプロンプトを出す。

アプリケーション(http://www.3rdparty.com)が
あなたのパーソナル情報(contactlist)に
読み取りアクセスを要求しています。

許可しますか?


はい  いいえ

ユーザはアプリケーションを確認したうえで、同意のボタンを押すか、拒否のボタンを押すことになる*2

同意のボタンが押された場合、CRMサービスは、同意した旨をポリシーに反映する。例えば以下のようなエントリをデータベーステーブルに追加する

+---------+---------------+-------------------------+
| user_id | resource_name | application_url         |
+---------+---------------+-------------------------+
| foobar  | contactlist   | http://www.3rdparty.com |
+---------+---------------+-------------------------+

ポリシーに上記エントリ追加した後、returnUrlパラメータで与えられたURLにリダイレクトすることにより、3rd partyのWebアプリに再び制御を移す*3

3rd Party Webアプリは、再びJSONのサービスエンドポイントにリクエストを送信する。

http://www.examplecrmservice.com/json/contactlist?callback=handleContactList

今度は先ほどと異なり、ポリシー設定が新たに追加されている。そしてそのポリシー中の application_url 列に送信されたリファラURLが含まれているため*4、アクセスは許可される。
結果、ユーザのコンタクトリストデータがレスポンスとして返される。

handleContactList([
  { "id" : 10000234,  "name" : "山田 太郎", "address" : "東京都千代田区丸の内2-4-1", "rank", "AA" } ,
  { "id" : 10000456,  "name" : "鈴木 一郎", "address" : "東京都港区六本木6-10-1", "rank", "A" } ,
  { "id" : 10000789,  "name" : "山本 花子", "address" : "東京都渋谷区桜丘町26-1", "rank", "B+" } ,
  { "id" : 10101012,  "name" : "佐藤 恵子", "address" : "東京都港区台場2-4-8", "rank", "S" } ,
  { "id" : 10101345,  "name" : "田中 伸一", "address" : "東京都港区赤坂5-3-6", "rank", "B-" } 
]);


とりあえず、以上のような方法でユーザの許認可ベースでパーソナルデータのやり取りが行えるようになる。流れを簡単にしたため、多少セキュリティ的に甘い部分も見受けられるかと思うが、3rd Partyアプリを事前登録制にするなどのあらかじめ制限を加えておくことによって回避できるものであると思っている。いずれは実装したデモを公開してみる予定。

(追記 8/30)Googleカレンダーのプライベートスケジュールのフィードを利用して、実験的に実装してみました。

*1:もちろん意図された通りにセキュリティ機構が実装されていることは重要

*2:ユーザが同意前にサービスポリシーを確認できるように、3rd Partyについて登録制にする必要もあるかもしれない

*3:このあたりのURLはあらかじめ登録制にするべきかもしれない

*4:ここでは単純に referer_url.startsWith(application_url) をチェックすることで、application_url で指定されたディレクトリ以下のアプリケーションからのJSONサービスアクセスを許している