ajaxでクロスドメインのAPIを叩く時にやったこと
アプリのweb viiewから、ajaxでクロスドメインのAPIを実行しようとして
とても大変な思いをしたので備忘録。
概要
GETが失敗する件
ログを見ると、OPTIONSリクエストに403を返していた。
What is OPTIONSリクエスト?
プリフライトリクエストという。
リクエストを送信しても安全か?サーバーがリクエストに対応しているか?
ということを調べるために、ブラウザが特定の条件を満たす場合に飛ばす。
プリフライトリクエストに対応する
以下のモジュールを使った。
Plack::Middleware::CrossOrigin - search.cpan.org
app.psgi
enable 'Plack::Middleware::CrossOrigin', origins => '*', headers => '*', methods => ['GET', 'POST'];
JSONPに対応する
What is JSONP?
- JSでは、クロスドメインにアクセスできない制限がある(同一生成元ポリシー)
- クロスドメインからJSをダウンロードすることは可能である。
- よって、クロスドメインから、APIの戻り値が入ったスクリプトを取得することは可能である。
という小細工仕組み。
JSONPで公開しているリソースは、誰からでも参照できてしまうので
機密情報を入れると情報漏えいのリスクがあり、注意が必要。
以下のモジュールを使った。
Plack::Middleware::JSONP - search.cpan.org
app.psgi
enable 'Plack::Middleware::JSONP';
他の対策案を考える
POSTでGET
語感だけは楽しそうだけど、いけてない感。
GETするのをやめる
HTMLを返すときに、APIの結果を埋め込めばいいんだ!
そして、埋め込まれた結果を使って、JSでDOMを弄くろう!
(いま考えると、なぜテンプレートを使おうという発想にならなかったのか)
加工済みのテンプレートを返す対応
結局こうなった。
POSTが失敗する件
Androidだけまた動かない。
OPTIONSで200 OKを返しているが、ブラウザでabortしている。
ここは理由が分からなかったので課題。
OPTIONSを飛ばさないでPOSTする分には動作していたので、
カスタムヘッダを削る&Content-Typeに「application/x-www-form-urlencoded」を指定で対応。
Access-Controll-Allow-Origin:"*"ってどうなのか
全てのホストからのリクエストを許可する設定はよくないので、Originを指定したいが
Androidのweb viewはリクエストのOriginがnullになっているというやる気のなさ。
しょうがないので、特定のリクエストの時のみ、CORSヘッダを付与するようにした。
まとめ
動いたことに満足しがちだけど、クロスドメインに紐づくセキュリティリスクも
きちんと知ることが重要。
あと、Androidのweb viewでクロスドメインは罠が非常に多かったので
jsをAPIと同じホストに置くのが一番良いのではないかと思う。
参考
http://www.w3.org/TR/cors/
https://developer.mozilla.org/ja/docs/HTTP_access_control
ここを熟読して、CORSとはなんなのかをよく学ぶべき。
http://www.atmarkit.co.jp/ait/articles/0908/10/news087.html
JSONPを使用するまえに熟読して、リスクを知るべき。