ほかの誰かが作ったCGIを移行する仕事で、バグの原因になったので記録しておく。
症状
CGIで作成された、お問い合わせフォーム。このCGIが出力するHTMLは、head要素内のscript要素でJavaScriptファイルを参照し、ユーザーインターフェースを制御している。
www有りと無しの二通りのURLでアクセスが可能(http://www.example.com/form/ と http://example.com/form/)。しかし、www有りでアクセスした場合は、JavaScriptが正しく動作しない。
原因と対応
JavaScriptファイルのうち、動的に生成されるファイルにバグがあった。動的に生成される部分というのは XMLHTTPオブジェクトがアクセスするURLであり、環境変数SERVER_NAME
を使って生成されていた。次のように。
$url = "\/\/" . $ENV{'SERVER_NAME'} . $service_path . '?'; $script =~ s/<url>/$url/g; # $scriptの中身はJSのテンプレ。それを置換している。 print "Pragma: no-cache\n"; print "Cache-Control: no-cache\n"; print "Content-type: text/plain; charset=UTF-8\n\n"; print $script;
SERVER_NAME
の値はWebサーバーの設定に依存してしまうため、JavaScriptファイルを取得するときのリクエストが www.example.com なのに対して、XMLHTTPオブジェクトは example.com を参照してしまうという矛盾が生じた。これはクロスドメイン制限(cross-domain restrictions)に抵触、あるいは同一生成元ポリシー(same-origin policy)に反してしまう。よって、次のように修正すればOK。
$url = "\/\/" . $ENV{'HTTP_HOST'} . $service_path . '?'; # HTTP_HOST に変更 $script =~ s/<url>/$url/g; (略)
SERVER_NAME と HTTP_HOST
CGIの環境変数のうち、SERVER_NAME と HTTP_HOST はよく似ていているのだが、意味が異なる。
これらの意味は、CGIに環境変数を渡しているApacheのドキュメントに書いてあるはずなのだが、いまいちみつからない。とりあえず、mod_rewriteの中に説明があったのでそこを引用しておく。
- mod_rewrite - Apache HTTP Server
-
- HTTP headers:
- (略)
- HTTP_HOST
- (略)
- server internals:
- (略)
- SERVER_NAME
- (略)
These variables all correspond to the similarly named HTTP MIME-headers, C variables of the Apache server or struct tm fields of the Unix system. Most are documented elsewhere in the Manual or in the CGI specification.
SERVER_NAME and SERVER_PORT depend on the values of UseCanonicalName and UseCanonicalPhysicalPort respectively.
- HTTP headers:
要するに、HTTP_HOST
のほうは"HTTP headers"
、つまりブラウザからのリクエストヘッダに含まれるHOSTを表し、SERVER_NAME
のほうは"server interanals"
、つまりApacheサーバー内の設定ファイル等に記述されているSERVER_NAMEを表すということ。したがって、HTTP_HOST
を扱うときはセキュリティ的に注意を要する、なんて話題も避けられない(変な文字列が送られてくる可能性があるから、内容をチェックしたりエスケープしたりする)。
このあたりの話は、Apacheから環境を引き継ぐプロセス全般に通用する話だから、PerlだけでなくPHPやRuby, Javaサーブレットなどを作るときにも頭に入っているとよいかもしれない。あと、mod_rewrite の条件を書く時にも。
0 件のコメント:
コメントを投稿