クロスドメインのセキュリティ問題を OAuth で解決する
ちゃんと検証してない思いつきレベルの話。
JSONP や Flash の crossdomain.xml を使ったクロスドメインアクセスで機密情報を取得する場合は、(何も対策をしないと)機密情報が外部に漏洩するという問題があるけど、この対策に OAuth が使えるんじゃないかと思った。
クロスドメインの制約
何だか前にも書いた気がするけど、思い出せないのでもう一度書く。
いわゆる Ajax (XMLHTTPRequest やその他の方法) を使えば、ある画面を表示しながらバックグラウンドで他の情報を取得することができる。 なるべく画面遷移をせずに処理を進めるのに便利なので、最近では良く使われるようになっている。 ただ、どのデータでも自由に取得できるかというとそうではなくて、たいていは「クロスドメインの制約」ってのがある。
例を挙げると、「はてなブックマーク」の画面を表示しているとき(Webブラウザのアドレスバーが http://d.hatena.ne.jp/ のとき)は、はてなブックマーク上のデータは動的に取得できる。 「GMail」の画面を表示しているとき(アドレスバーが http://mail.google.com/ のとき)は、 GMail 上のデータを動的に取得できる。 でも、「はてなブックマーク」の画面を表示しているときに、動的に GMail のデータは取得できないようになっている。 もし取得できちゃうと、はてなブックマークの管理者が悪意のあるスクリプトを仕掛けることで、利用者の GMail の内容を読むことができるようになってしまうから。
これがクロスドメインの制約。
で、抜け道として JSONP のデータを <script src> で取得したり、 crossdomain.xml を設定して Flash でデータを取得したりって方法がある。
クロスドメインのセキュリティ
ドメインを超えてデータにアクセスできると便利なことがたくさんある。 WebAPIを公開して、JavaScript経由でアクセスできるようにするとかね。 でも、もともと禁止されていることを可能にしているのだから、何も考えないと当然問題になる。
データが公開情報(Google Mapsの地図とか)であれば、クロスドメインでアクセスされても特に困らない。 でも、機密情報(個人のメールとか連絡先とか)の場合は、無条件にクロスドメインでアクセスされると困る。 さっきのはてなブックマークとGMailの例のようにね。
だから、クロスドメインで機密情報を公開するときは、ちゃんと対策を考えないといけない。 以下参考。
- Kazuho@Cybozu Labs: クロスサイトのセキュリティモデル
- ここが危ない!Web2.0のセキュリティ:第3回 JSONPでのクロスドメインアクセス
- JSONPはセキュアでないのか? - snippets from shinichitomita’s journal
- Web 2.0的アプリのセキュリティ:機密情報にJSONPでアクセスするな
- Web 2.0的アプリのセキュリティ:再考「機密情報にJSONPでアクセスするな」
クロスドメインでのアクセスに OAuth トークンを使う
この問題の本質は、「意図しないサイトに機密情報が公開されてしまう」ってことだと理解している。 なので、対策として「許可したサイトにだけ機密情報を公開するようなアクセス制御の仕組みを用意」すればいい。 例えば、 Flash の crossdomain.xml はアクセスを許可するサイトを明記できるようになってる*1。
ただ、この方法だと動的にポリシーを切り替えることが難しい。例えば、ユーザごとに許可するドメインを変えるということができない(…よね?動的にcrossdomain.xmlを出力すればできる?)。 WebAPI を使うシーンでの「機密情報」ってのは、「利用者の個人的な情報」な訳だから、どのサイトに公開するかはユーザが決められるべき。 サイトの管理者が公開先をいちいち crossdomain.xml に書くような仕組みだとスケールしない。
んで、「許可したサイトにだけ機密情報を公開するようなアクセス制御の仕組み」ってとこで、WebAPI のアクセス制御に使える OAuth という仕様という話を思い出したという訳。
OAuth で登場するのは、次の三者になる。
- Provider … OAuth API の提供者。 WebAPI でユーザの機密情報を公開する。 Twitter や Flickr などが該当。
- Consumer … OAuth API の利用者。 JSONP などで公開されているユーザの機密情報をユーザの許可を得て取得する。
- ユーザ
普通の OAuth の使い方だとこうなる。
- Consumer が Provider に WebAPI を利用するためのトークンを要求する
- Consumer へのトークンの発行をユーザが許可する。
- Provider が Consumer へトークンを発行する
- Consumer が WebAPI 経由で Provider 上のユーザの機密情報を取得する(発行されたトークンを一緒に送る)
ユーザの JavaScript 経由で WebAPI を使うときにも、同じように OAuth のトークンを付けてあげればいい。 OAuth のトークンを付けられるのはユーザが許可した Consumer だけ。他のサイトは OAuth トークンを知らないので、ユーザの機密情報を取得できないって訳。
デメリットは WebAPI を呼び出すたびに OAuth トークンへ署名をつけなきゃいけないことかな。 署名を付けるための鍵は Consumer が持っているので、
- ユーザは Consumer にトークンと署名を要求する
- Consumer は鍵を使ってトークンに署名を付与する
- ユーザは WebAPI 経由で Provider 上の機密情報を取得する(トークンと署名を一緒に送る)
という手間が必要になる。
まとめ
OAuth を使えば、クロスドメインで機密情報を公開するときのセキュリティ対策になるかもって話だった。 図を描かなかったので、分かりにくかったかも。
それから、クロスドメイン問題にはそれほど詳しくないので、間違いがあったら指摘をお願いします。