mod_rewrite を使うときには 404 の扱いに注意
Posted by yoosee on Web at 2007-04-03 22:00 JST1 404 Blog Not Found:誤った404エラーページをつくるただ1つの方法
言ってることはもっともなんだけど、例を見たときには何を問題視しているか分からなかった。誤った404エラーページをつくるただ1つの方法、それはStatus 404を返さないことなのです。と言うのも、例示が % HEAD -S http://caramel-tea.com/404.php となっていたから。存在するコンテンツなのだから 200 が返るのは当然だ。と言うことで実際には存在しない http://caramel-tea.com/foo/bar/aa へアクセスしてみると
ところが、未だにエラーなのにStatus 200を返すURIがあふれています。404 Blog Not Found:誤った404エラーページをつくるただ1つの方法
% HEAD -S http://caramel-tea.com/foo/bar/aa HTTP/1.1 200 OK Date: Tue, 03 Apr 2007 23:14:22 GMT Server: Apache Cache-Control: no-cache, must-revalidate, max-age=0 Expires: Wed, 11 Jan 1984 05:00:00 GMT Pragma: no-cache X-Pingback: http://caramel-tea.com/xmlrpc.php Status: 404 Last-Modified: Tue, 03 Apr 2007 23:14:28 GMT Connection: close Transfer-Encoding: chunked Content-Type: text/html; charset=UTF-8おお、こちらも確かに 200 が返ってる。これはよろしくない。ちなみに netcraft で調べても OS と Webサーバが不明だが、ホストは xrea.com のものらしいので スペック を見る限りは Linux / Apache らしい。
ご存知だろうが Apache ならば設定ファイルや .htaccess に
ErrorDocument 404 /404.htmlと書けば、static な URI のコンテンツを Status Code 404 で表示させることが出来る。例にそう設定してアクセスしてみると
% HEAD -S http://yoosee.net/foo HTTP/1.1 404 Not Found Date: Tue, 03 Apr 2007 23:00:31 GMT Server: Apache/1.3.37 (Unix) Last-Modified: Tue, 03 Apr 2007 22:59:06 GMT ETag: "33921c-76-46142dba;46142de6" Accept-Ranges: bytes Content-Length: 118 Content-Type: text/htmlHTTP コードとして 404 が返っており、問題ない。ちなみに ErrorContent に http:// で始まる URI を入れた場合には 302 で他所に飛ばされるようだ。
HTTP/1.1 302 Found Date: Tue, 03 Apr 2007 01:42:52 GMT Server: Apache/1.3.37 (Unix) Location: http://yoosee.net/404.html Connection: close Content-Type: text/html; charset=iso-8859-1これはこれでよろしくないが、caramel-tea.com の例とはまた違う。予測できるのは、恐らく caramel-tea.com では mod_rewrite で存在しないページを別ページに飛ばしているのだろう。
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ /404.php [L]こんな感じでファイルやディレクトリの存在確認演算子 -f や -d を使えば、存在しなかった場合の RewriteRule を書くことが出来る。手元で試したものは ErrorDocument 404 が指定されていても 200 が返されたので、恐らくこれが原因ではなかろうか。
ちなみに呼び出し先を cgi にして
#!/usr/bin/env ruby require 'cgi' cgi = CGI.new puts cgi.header("type" => "text/html", "status" => "NOT_FOUND") puts "<html><body>404 not found.</body></html>"のように CGI 側で明示的に 404 を返した場合には、Rewrite 経由でもきちんと 404 が返った。php でのやり方は知らないが、同様にステータスコードは返せるのではないかと思う。
ちなみに Google への対処策としてはGoogle ウェブマスター向けヘルプセンター - 存在しないページに対してサーバーから 200 (見つかりました) ステータスが返されました にあるように meta タグで回避することも出来るが、他のロボットも考えればきちんと HTTP のレスポンスコードで 404 を返すべきだ。そうしておけば、ログ解析時に 404 が発生している事を確認するといった作業が容易く行えると言うメリットもある。
全信協spamクローラのフィルタ
Posted by yoosee on Web at 2006-04-04 23:42 JST1 所謂 全信協スパムクローラを避ける mod_rewrite 設定
日本の spam crawler としては老舗であろう全信協だが、1秒間に 20アクセス以上と言うふざけたアクセスをしてくる上に index.html へ連続 15回 といった無意味なアクセスも大量にしてくるのがいい加減に鬱陶しいので、今更ではあるがフィルタすることにした。対策としては mod_rewrite による deny。全信協スパムクローラ対策 を参考に RewriteRule を書いた。OCN など特定 ISP が良く使われるという特徴の他にも
- HTTP/1.0 でのアクセス
- HTTP_REFERER が空
- USER_AGENT が Mozilla/4.0 (compatible; MSIE 6.0; Windows 98)
(ないし Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.1.4322) もあるらしいが、後者はうちではあまり見当たらない)
RewriteEngine On RewriteCond %{HTTP_REFERER} ^$ RewriteCond %{REMOTE_HOST} marunouchi\.tokyo\.ocn\.ne\.jp$ [OR] RewriteCond %{REMOTE_HOST} tokyo-ip\.dti\.ne\.jp$ [OR] RewriteCond %{REMOTE_HOST} odn\.ad\.jp$ [OR] RewriteCond %{REMOTE_HOST} tky\.mesh\.ad\.jp$ [OR] RewriteCond %{REMOTE_HOST} ap\.gmo-access\.jp$ RewriteCond %{HTTP_USER_AGENT} "^Mozilla/4\.0 \(compatible; MSIE 6\.0; Windows 98\)$" RewriteCond %{SERVER_PROTOCOL} ^HTTP/1\.0$ RewriteRule .* - [F,L]ちなみに RewriteRule は下層ディレクトリにも引き継がれるが、そちらで別途 RewriteEngine On による Rewrite 設定をしている場合には
RewriteEngine On RewriteOptions inheritとしておかないと設定がクリアされて効かなくなるらしいので注意。
apache2 で allow, deny が効いていなかった
Posted by yoosee on Debian at 2005-10-18 23:42 JST1 apache2 で allow, deny が効いていなかった
Debian の apache2-mpm-prefork 2.0.54-5 で deny from all 等の設定が効かなくなっていた。色々試した結果、どうやら apache2.conf に問題があったようで、apache2.conf と apache2.conf.dpkg-dist の diff を取ってみると怪しい部分があった。<Location "/"> <LimitExcept GET HEAD POST OPTIONS> Deny from all </LimitExcept> </Location>これか…。多分自分で足したものだと思うけど、これ自体は GET, HEAD, POST, OPTION 以外のリクエストを deny しろという命令のつもりなのだが、逆に GET, HEAD, POST, OPTION に関しては allow from all 的動作をしていたっぽい。まぁ試験用サイトだから良いようなものの、間抜けな私。
Apache での WORM_AGOBOT 対策
Posted by yoosee on Debian at 2004-05-24 23:42 JST1 WebDAV 狙いの WORM アクセス
1,2 週間程前から、Windows プラットフォームの WebDAV を狙ったらしい SEARCH メソッドのアクセスが apache の log に大量に残るようになった。例えばこんな感じだ。218.xx.xx.xx - - [21/May/2004:09:57:47 +0900] "SEARCH /\x90\x02\xb1\x02\xb1\x02\xb1\x02\xb1\x02\xb1\x02\xb1\x02\xb1\x02\xb1\.....このリクエストは 1 アクセスが 32KB もあるので性能的にもログ的にも鬱陶しくて仕方がない。
2 アクセスログの分離
根本対策にはならないが、access.log が肥大化して鬱陶しいため、まずはログを分離する。AGOBOT の場合、Nimda のような SetEnvIf での分離は難しい。しかしこの手の WORM は HTTP/1.1 の Host: 無しで IP アドレスに対してアクセスしてくるので、VirtualHost で IP アドレスへのアクセスを分けてやるのが有効らしい。NameVirtualHost xx.xx.xx.xx <VirtualHost xx.xx.xx.xx> ServerName dummy DocumentRoot /home/www/defaults/www/ CustomLog /var/log/apache/defaults/full.log full ErrorLog /var/log/apache/defaults/error.log </VirtualHost> <VirtualHost yoosee.net> ServerName yoosee.net ServerAlias www.yoosee.net yoosee.net ServerAdmin webmaster@yoosee.net DocumentRoot /home/www/yoosee.net/www/ CustomLog /var/log/apache/yoosee.net/full.log full ErrorLog /var/log/apache/yoosee.net/error.log </VirtualHost>といった感じで log を分離する。log が必要ない場合は Log ファイル名を /dev/null にしてしまうと良い。
3 SEARCH Method の拒否
そもそも SEARCH Method は WebDAV 用の method なので apache で処理する必要はない。逆に apache には無い method なので単純に Limit での拒否はできない。と言うことで LimitExcept で使える method をそもそも限定してしまうことにする。<Location /> <LimitExcept GET POST HEAD OPTIONS> Deny from all </LimitExcept> </Location>ただこのやり方だと結局 Request を受けた後で 500 なりを返すので、性能的にはあまり改善され無い気がする。
4 根本的対策
以上の方法は apache 側での処理のため、根本的な負荷軽減にはあまり効かないと思われる。根本的に対策するには、前段に Squid 等で Reverse Proxy を入れたうえで Squid 側の acl で method を制限する、または Guardian のような Reverse Proxy 型 アプリケーション ファイヤウォールで防衛する、等の方法があるようだ。同一 IP からのアクセスが多い場合には apache 側で mod_throttle 等を使う方法もある気がするが、これも一度 apache 側で処理を受けてしまうため効果のほどは微妙。アクセスをトリガにして何か処理をしたい場合には mod_security を使う方法もあるが、今回向きでは無いだろう。