Flashを用いたクロスドメインアクセス
前回までは、クロスドメインアクセスを行うための方法として、リバースProxy を使う方法とJSONP を使う方法を紹介しましたが、どちらの方法も少し変わった方法だったと思います。なにか無理やりのように感じた方もいるのではないでしょうか。今回紹介するFlashを使った方法 では前回までの方法とは違い、自然な形でクロスドメインアクセスを行うことができます。
Flashでは、呼び出される側で設定を行うことでクロスドメインアクセスが可能になります。
設定といっても非常に簡単で、呼び出される側のWebサーバにcrossdomain.xml というファイルを設置するだけです。このときのURLは
http://www.example.com/crossdomain.xml
となります。
ファイルの内容は以下のようになります。
crossdomain.xmlの内容
<cross-domain-policy>
<allow-access-from domain="www.securesky-tech.com" />
</cross-domain-policy>
このように設定ファイルを記述すれば、www.securesky-tech.comからのクロスドメインアクセスを許可するという設定になります。
それでは、Flashではどのような順序でクロスドメインアクセスが行われるかを見ていきます。
図1 Flashでのクロスドメインアクセス
まずユーザはFlashが置かれているサイトへアクセスしに行きます(1) 。このFlashファイルにクロスドメインアクセスを行う命令が書かれています(2) 。このとき、ブラウザはクロスドメインアクセスを行う前に、アクセス先のサイトからcrossdomain.xml を取得します(3) 。このファイルで許可設定がされている場合にのみ、クロスドメインアクセスが行われます(4) 。
XMLHttpRequestオブジェクトの場合にクロスドメインアクセスが完全に禁止されていたのとは対象的に、Flashの場合は、許可するか禁止するかを、設定ファイルによって変更することができるようになっています。ディレクトリごとに設定を変えたい場合には、サブディレクトリごとにcrossdomain.xmlファイルを置くことができます。なお、crossdomain.xmlファイルを設置していない場合はクロスドメインアクセスは行われません。
Flashでのクロスドメインアクセスを認める場合のセキュリティ
もしcrossdomain.xmlで許可設定をした場合、他のファイル(たとえばログイン後の画面)に対してまでクロスドメインアクセスができてしまいます。つまり、悪用されるとユーザの情報が盗まれてしまう ことになります。許可設定を行う場合には、他のページで機密情報を扱っていないかを十分確認するようにしてください。
とくに、WebAPIを公開する際には注意が必要です。WebAPIを公開し、Flashからも利用してもらいたい場合、crossdomain.xmlファイルは以下のようになります。これはすべてのドメインからクロスドメインアクセスを認める という設定になります。
WebAPIを公開する場合のcrossdomain.xmlの内容
<cross-domain-policy>
<allow-access-from domain="*" />
</cross-domain-policy>
このとき、もし同じサイトで他のサービスを提供していた場合には、そのサービスに対してもクロスドメインアクセスが可能になってしまいます。その結果、第2回 で説明したように、クロスドメインアクセスを悪用した情報漏えいが発生します。
このようなことを防ぐためには、まずクロスドメインでアクセスされることを許可するサービスと、そうでないサービスを明確にしておく 必要があります。その上で、それぞれのサービスを別のドメインで提供するようにします。たとえば既存のサービスのドメインがwww.securesky-tech.comとなっている場合には、api.securesky-tech.comというドメインを作り、WebAPIを提供するようにします。また、サブディレクトリで分けることでも対策となります。
JSONの動作原理
前回、JSONPを紹介しましたのでJSON も紹介しておきたいと思います。JSONPとはJSON with Paddingの略ですので、JSONPの元になったのがJSONです。JSONPとの違いはコールバック関数が定義されていなく、JavaScriptのオブジェクトのみ が記載されているという点です。このデータを読み込み、評価(eval() )して使います。
JSONの利点は、読み込んだデータをいちいち解析することなく、eval()関数で評価するだけで使用できるという点です。
図2 JSONの概念図
object.dat
[{ "name" : "Fukumori" }]
<html>
<script>
var req;
if( window.XMLHttpRequest){
req = new XMLHttpRequest();
}else if(window.ActiveXObject){
try {
req = new ActiveXObject("MSXML2.XMLHTTP");
} catch (e) {
req = new ActiveXObject("Microsoft.XMLHTTP");
}
}
if (req) {
req.open('GET', 'object.dat'); // JSONデータを取得
req.onreadystatechange = function() {
if (req.readyState == 4) {
var obj = eval("("+req.responseText+")"); // 評価して使用
alert(obj[0]["name"]);
}
}
req.send(null);
}
</script>
</html>
JSONのセキュリティ
JSONはSCRIPTタグで呼び出しても使用できないため、JSONPという方法が生まれました。
しかし皮肉なことに、少し工夫することで、JSONのデータをSCRIPTタグから呼び出して使うことができてしまいます。つまり、JSONでもSCRIPTタグで呼び出してクロスドメインアクセスが可能であり、悪用することも可能であるということになります。
それでは、JSONをSCRIPTタグから呼び出して使用する方法を説明します。
setterメソッド をサポートしているブラウザ(FirefoxやSafari 3等)では、setterメソッドを再定義することによって、JSONもSCRIPTタグで呼び出して使うことが可能です。setterメソッドとは、プロパティに値がセットされるときに呼び出されるメソッドです。
<html>
<script>
Object.prototype.__defineSetter__('name', function(x){sendtoAttacker(x);});
</script>
<script src="http://www.securesky-tech.com/object.dat"></script>
</html>
このようにすることで、JSONであってもクロスドメインでアクセスして情報を取得することができます。JSONPで説明した方法と同様の方法を使うことで、悪用も可能です。
JSONのセキュリティ対策
JSONデータへクロスドメインでアクセスする方法が存在するため、JSONデータの中に機密情報を含めることはできません。しかし、クロスドメインアクセスをされないように工夫することで、機密情報を含めて提供することができます。クロスドメインアクセスされないようにする手段として、XMLHttpRequestオブジェクト からの読み込みは許可 し、SCRIPTタグ からの読み込みは拒否 するような工夫を行います。
・JSONPの場合と同様の対策を行う
JSONPの場合と同様に、クエリストリングに認証情報を含める方法や、Refererヘッダをチェックする方法が使えます。クロスドメインでの意図しないアクセスを防ぐことが対策になるためです。
・JavaScriptとして読み込めないようにする
ファイルの先頭に while(1); を記述します。すると、もしSCRIPTタグから読み込まれた場合にはブラウザが無限ループに陥り、次の行が実行されることはなくなります。または、全体をコメントアウト記号(/* ... */ )でくくります。コメントアウトされた部分は実行されなくなります。正規のユーザが使用する場合には、XMLHttpRequestオブジェクトでJSONファイルを取得し、whileやコメントアウト記号を取り除いて使用します。
・POSTリクエストに限定する
SCRIPTタグからJSONファイルを呼び出した場合にはGETリクエスト になります。そこで、呼び出されるJSONファイルでは、POSTリクエストのみ受けつける ようにします。このようにすることで、SCRIPTタグからの読み出しを拒否することができます。
・リクエストヘッダを付加する
XMLHttpRequestオブジェクトからリクエストを発行する際に、ヘッダを付加します。SCRIPTタグから呼び出す場合にヘッダを付加することはできませんので、SCRIPTタグからのリクエストなのかそうでないのかを、サーバ側で判断することができます。
実際には、以下のようにして付加することができます。
req.setRequestHeader("X-Requested-With", "XMLHttpRequest");
どの方法もその場しのぎような感じがあります。JSONは2006年にRFCになったばかりのまだ新しい方式ですので、今後のセキュリティ対策に注目したいところです。