2010-01-18 2010-01-18
●iモードブラウザ2.0のXMLHttpRequestでPOSTデータの扱いが困難になった
このエントリでは、iモードブラウザ2.0の制限により、XMLHttpRequestでPOSTメソッドの利用が困難になっていることを確認したので報告する。
iモードブラウザ2.0のJavaScriptを試していて、POSTメソッドでデータが渡せていないことに気がついた。以下のようなプログラムで検証してみた。
【post.html】
<html>
<head>
<script>
function test() {
try {
var requester = new XMLHttpRequest();
requester.open('POST', '/dumppost.php', true);
requester.onreadystatechange = function() {
if (requester.readyState == 4) {
onloaded(requester);
}
};
requester.setRequestHeader("Content-Type" , "application/x-www-form-urlencoded");
requester.send("aaa=bbb&ccc=ddd");
} catch (e) {
res = requester.responseText;
document.getElementById('result').innerHTML = e.toString();
}
}
function onloaded(requester) {
res = requester.responseText;
document.getElementById('result').innerHTML = res;
}
</script>
</head>
<body>
<input type=button value="go" onclick="test()">
<div id="result"></div>
</body>
</html>
【dumppost.php】
<?php
echo "aaa=" . htmlspecialchars($_POST['aaa'], ENT_QUOTES, 'Shift_JIS') . "<br>";
echo "ccc=" . htmlspecialchars($_POST['ccc'], ENT_QUOTES, 'Shift_JIS') . "<br>";
?>
実行結果は、以下のようになった。まずはChromeのものだが、IEやFirefoxでも同等の結果だ。
次に、ドコモP-07Aによる結果
ドコモの場合を検証するために、Webサーバーに来ているリクエストをキャプチャしてみた。
POST /dumppost.php HTTP/1.1 X-UE-Version: 1 Host: XXXXXXXXXXXXX User-Agent: DoCoMo/2.0 P07A3(c500;TB;W24H15) Content-Type: text/xml Content-Length: 15 aaa=bbb&ccc=ddd
ご覧のようにPOSTデータそのものは送信されてきているが、Content-Typeがtext/xmlになっているために、Webアプリケーション側で受け取れないようだ。
JavaScript側では、この値をapplication/x-www-form-urlencodedに変更しているが、P-07AでもJavaScriptが再開され、あらたな制限がみつかったで報告したように、setRequestHeaderが無効化されているために、この設定が無視されていることが原因のようだ。
これは明らかに、setRequestHeader無効化の副作用であるが、その代償は大きいように思う。PHP以外に、ASP/ASPX、J2EE(JSP)にこのデータを入力してみたが、いずれも値を読み取ることはできなかった。一方、PerlのCGIモジュールでは、POSTDATAという名称のデータとして、POSTデータ全体を読み取ることができた。Perl以外の場合でも、Webサーバーにデータ自体は到達しているのだから値を利用する手段はあるかもしれないが、私が調べた範囲では分からなかった。
上記の結果として、AjaxでPOSTメソッドを扱うには、標準的でない方法を用いる必要があるわけだが、その方法を検討してみた。
- POSTをあきらめてGETを使う
- Perlのように、text/xml形式のデータを読み出せる言語を選択する
- DOMにより、IFRAME内にFORMを作成してSUBMITする
このうち、上記1.については、セキュリティ上の問題が発生し得る。Ajaxのセキュリティ対策として、「GETメソッドを拒絶する」という方法があるからだ。具体的には、SCRIPT要素を使ってSame Origin Policyを回避してAjaxデータを読み出す手法に対抗して、SCRIPT要素では必ずGETメソッドになることから、POSTのみを許容することで対策するという方法だ。既存のアプリケーションがこのような手法によりセキュリティ対策されている場合に、安易にGETメソッドを許容してしまうと、セキュリティホールが混入することになりかねない。
3.については、私が試した範囲ではうまくいっていない。JavaScriptからIFRAME内のFORM操作がうまくいかないのだ。これも、ひょっとすると制限を掛けているのかもしれない。アドホックな方法ならありそうだが、まだ十分に検証できていない。
一つの疑問は、このような情報がインターネット上に見あたらないことだ。AjaxでPOSTメソッドが使えないというのはとんでもないことだが、検索してもそのような情報がみあたらないのだ。iモードブラウザ2.0のAjaxを誰も使っていないのか、それとも事業者が恐ろしくて口をつぐんでいるのか。
しかし、このような技術情報が流通していかない限り、iモードブラウザ2.0のJavaScriptを使ったコンテンツは普及していかないだろう。NTTドコモにはさらなる情報開示を期待したいし、ケータイWebの開発者にも、もっとブログなどでの情報公開を期待する。
formのactionに直接パラメータを積むとか?
xml形式のデータをsubmitしたら読み取れる?
masaさん、twkさん、コメントありがとうございます。<br>formのactionにパラメータ積んだら、それは実質的にGETということになりませんか?<br>xml形式のデータをSUBMITというのは、実はやってみたのですが、PHP、ASP/ASP.NETなどではうまく受け取れていません。やり方があるのかもしれませんが、私は知りません。
PHP なら、php://input でデータを取得できるのではないでしょうか?<br><br>file_get_contents('php://input');<br><br>http://www.php.net/manual/ja/wrappers.php.php
Kenjiさん、コメントありがとうございます。php://inputというものがあるのですね。確認したところ、確かにデータを取得できました。
サーバサイドJavaの場合、ServletRequestのgetInputStreamからストリームとして取得できます
ASP.NETの場合なら、HttpContext.Current.Request.InputStreamから読めると思います。
ストリームとして取得して自分で分解する方法で対応しようと思えば出来る状況だとしても、(それまでに比べて)手順がフクザツなので、「あきらめてGETを使う」に流れてしまう開発者も多そう。setRequestHeaderが少なくとも"Content-Type"に対しては今までどおり動作するように再改修されることが望まれますね。。
記事とは関係ないコメントですいません<br><br>http://nat-q.jp/ctg3/q_detail.php?no=10340<br>ユーザ同士で質問を回答するサイトの1ページなのですが<br>iモードブラウザ2.0ブラウザによる乗っ取りなのでしょうか?<br>自分では判断できないので検証していただきたいです<br>(ちがうなら、あたしの勘違いでいいのですが<br>あたりなら実害が出ているとドコモなどへ報告していただきたいです)
姫さん<br><br>質問を見てみましたが、これはご本人がドコモショップに相談に行くのがよいと思います。あくまで想像ですが、のっとりなどではないような気がします。<br>最近は携帯電話を購入すると、必要のないコンテンツを購入させられて「いらなければ直ぐに解約してください」と言われることがよくあります。そのたぐいの話で、ショップの店員が説明していなかったとか、説明を聞き漏らしていたとか、理由は無数に考えられます。
コメントいただいた皆様、ありがとうございました。<br>さすがに、言語毎に方法はあるのですね。<br>ですが、あまり知られていない方法を使わないといけないこと、そのための説明などがドコモの解説などにないことから、やはりPOSTデータの扱いは「困難」だなと思います。<br>この問題は引き続きウォッチしたいと思います。ありがとうございました。