XMLHttpRequestがsame origin policyに縛られているのをなんとかするためのプロキシをmod_proxyだけでやるという話.
XHRとクロスドメイン
XMLHttpRequestを使うと, JavaScriptを積極的に使ったダイナミックHTMLのページから, 新たなデータを動的にリクエストすることができ, 取得したデータを元のページ内で画面遷移なしに利用できる. いわゆるAjaxの要となる技術.
ただし, XMLHttpRequestは基本的に同一ドメインに対してのリクエスト以外は無効という制限がある. もしこの制限がないとセキュリティ上の問題が発生する. たとえばGmailのURLに対してXMLHttpRequestでリクエストを行なうコードをどこかのサイトに仕掛けておくとする. このXMLHttpRequestでは, 認証情報を含むCookieもブラウザによって送信される. すると, Gmailにログインしている人がそのサイトを訪れる度に, その人のメールボックスや連絡先を盗み取れることになる.
そうは言うものの, 同じWebサービスでもドメインを跨いで運用している場合もあり, 同じドメインでないといけないという制約はきつすぎる場合もある. 最近はこれを緩和して, ドメインを跨ぐリクエストを明示的に許可できるようにする方法(XMLHttpRequest Level 2, Cross-Origin Resource Sharing)もあり, リクエスト先のサーバからのHTTPレスポンスのAccess-Control-Allow-Origin
等にリクエスト元が含まれていればドメインを跨いでいてもレスポンスが得られる仕組みになっている.
とくに公開APIを提供するWebサービスで, かつユーザ認証を伴なわないものについては, すべてのアクセス元からのXMLHttpRequestを許すべき. 逆にそのようなAPIを利用したいだけの場合には, Cookieの送信をしない代わりにクロスドメインのXMLHttpRequestを許すような仕組みがあってもよさそうなところ.
同一ドメイン制約をなんとかする方法
クロスドメインでXHRするための方法としては以下のものが知られている.
- JSONP
- もちろんサーバ側がcallbackを受け付ける場合のみ
- データもJavaScriptとして解釈できるものに限られる
- Flashを使う
- サーバ側にポリシーファイルを置く必要あり
Access-Control-Allow-Origin
等を設定する- クロスドメインリクエストプロキシ
最初の3つはXHRのリクエスト先サーバによるお膳立てが必要. そういう仕掛けが望めない場合には, ドメインを跨ぐためのプロキシを立てるという方法がある.
XDRプロキシ
方法としては単純で, 自分の管理下にあるプロキシサーバに代わりにリクエストしてもらい, そのプロキシサーバからのレスポンスではAccess-Control-Allow-Origin
であらゆるリクエスト元を許可するようにする. プロキシサーバのドメインはふつうリクエスト先のサーバとは別なのでCookieは渡らず, 「Cookieの送信をしない代わりにクロスドメインのXMLHttpRequestを許すような仕組みがあってもよさそう」というのをまさに体現したものということになる.
このようなプロキシサーバの実装例としてはたとえば404 Blog Not Found:Ajax - Goodbye, JSONP. Hello, Access-Control-Allow-Originがあるけれど, Perlのスクリプトで実装というのはなんともイケてない感じがする. どうせApache等で動かすのだろうから, HTTPサーバのプロキシ機能でなんとかしたい.
Apache 2.2のmod_proxyにはProxyPassMatch
というのがあるので, 実は簡単にできる. Perlなんて書かなくてもよかったんや! (Accss-Control-Allow-Origin
の設定のためにmod_headersも使う.)
<VirtualHost *:80> ServerName xdr.example.org ProxyPassMatch ^/http://(.*)$ http://$1 <Proxy *> Allow from all </Proxy> Header add Access-Control-Allow-Origin "*" </VirtualHost>
あるいは2.2.6より古い場合はmod_rewriteを併用して
<VirtualHost *:80> ServerName xdr.example.org RewriteEngine On RewriteRule ^/http://(.*)$ http://$1 [P] <Proxy *> Allow from all </Proxy> Header add Access-Control-Allow-Origin "*" </VirtualHost>
などとする. こういうものを用意しておいて, http://example.net/hogehoge
へXMLHttpRequestする代わりにhttp://xdr.example.org/http://example.net/hogehoge
へXMLHttpRequestするようにすれば, ドメインを跨いでいても成功する.
上記の設定はオープンプロキシになっているので, 悪用されたらまずいと思うならAllow from
のところを適宜制限する.
参考資料
あとで調べたらApacheの設定方法も全く同じものが公開されていた:
追記: 2013-11-02
アクセス先がリダイレクトされてしまうと, Location
ヘッダはそのまま返ることになって, ブラウザが改めて別ドメインにアクセスしようとして失敗してしまう. これを避けるには, レスポンスのLocation
ヘッダを書き換えればよい:
Header edit Location ^(.*)$ http://xdr.example.org/$1
Header
ディレクティブのedit
は2.2.4からサポートされている.