Tuesday, December 31, 2013, 18:04 - Misc
Posted by ELIN
Webサーバを新しく下記のような設定で組み直すことにした
<VirtualHost *:80>
DocumentRoot /var/www

RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}/%{HTTP_HOST} -d
RewriteRule . %{DOCUMENT_ROOT}/%{HTTP_HOST}%{REQUEST_URI} [L]
RewriteRule . https://www.google.co.jp/search?q=%{HTTP_HOST}%{REQUEST_URI}
</VirtualHost>

要するにこれはディレクトリを掘るだけで擬似的にバーチャルホストとして運用できるようにするもので、これ自体は検索すればすぐに見つかる程度の安易な方法ではあるが、ともかくずぼらな私には好ましい方法である

しかしこれはmod_rewriteの深い闇を見る

この時点での挙動は
(2) init rewrite engine with requested uri /foo.html
(3) applying pattern '.' to uri '/foo.html'
(4) RewriteCond: input='/var/www/www.test1.com' pattern='-d' => matched
(2) rewrite '/foo.html' -> '/var/www/www.test1.com/foo.html'
(2) local path result: /var/www/www.test1.com/foo.html
(1) go-ahead with /var/www/www.test1.com/foo.html [OK]

と、何の問題もなく、期待通りの挙動をしている

しかしこの方法はhttpd.confへmod_rewriteへの設定を書けないため、特定ホストでのみ運用したいルールは.htaccessで賄う必要がある
そこで.htaccessへ下記のような設定を追加すると
RewriteEngine On
RewriteRule foo/(.*) bar/$1 [L]

(2) init rewrite engine with requested uri /foo/index.html
(3) applying pattern '.' to uri '/foo/index.html'
(4) RewriteCond: input='/var/www/www.test1.com' pattern='-d' => matched
(2) rewrite '/foo/index.html' -> '/var/www/www.test1.com/foo/index.html'
(2) local path result: /var/www/www.test1.com/foo/index.html
(1) go-ahead with /var/www/www.test1.com/foo/index.html [OK]
(3) [perdir /var/www/www.test1.com/] strip per-dir prefix: /var/www/www.test1.com/foo/index.html -> foo/index.html
(3) [perdir /var/www/www.test1.com/] applying pattern 'foo/(.*)' to uri 'foo/index.html'
(2) [perdir /var/www/www.test1.com/] rewrite 'foo/index.html' -> 'bar/index.html'
(3) [perdir /var/www/www.test1.com/] add per-dir prefix: bar/index.html -> /var/www/www.test1.com/bar/index.html
(2) [perdir /var/www/www.test1.com/] strip document_root prefix: /var/www/www.test1.com/bar/index.html -> /www.test1.com/bar/index.html
(1) [perdir /var/www/www.test1.com/] internal redirect with /www.test1.com/bar/index.html [INTERNAL REDIRECT]
(2) init rewrite engine with requested uri /www.test1.com/bar/index.html
(3) applying pattern '.' to uri '/www.test1.com/bar/index.html'
(4) RewriteCond: input='/var/www/www.test1.com' pattern='-d' => matched
(2) rewrite '/www.test1.com/bar/index.html' -> '/var/www/www.test1.com/www.test1.com/bar/index.html'
(2) local path result: /var/www/www.test1.com/www.test1.com/bar/index.html
(1) go-ahead with /var/www/www.test1.com/www.test1.com/bar/index.html [OK]
(3) [perdir /var/www/www.test1.com/] add path info postfix: /var/www/www.test1.com/www.test1.com -> /var/www/www.test1.com/www.test1.com/bar/index.html
(3) [perdir /var/www/www.test1.com/] strip per-dir prefix: /var/www/www.test1.com/www.test1.com/bar/index.html -> www.test1.com/bar/index.html
(3) [perdir /var/www/www.test1.com/] applying pattern 'foo/(.*)' to uri 'www.test1.com/bar/index.html'
(1) [perdir /var/www/www.test1.com/] pass through /var/www/www.test1.com/www.test1.com

となり、結果404が返されることになる
internal redirectによって再度httpd.confのルールが評価されるために、この問題は発生する

またこのinternal redirectによる問題は似たような別の問題も引き起こす
(2) init rewrite engine with requested uri /foo/!"#$%&'().txt
(3) applying pattern '.' to uri '/foo/!"#$%&'().txt'
(4) RewriteCond: input='/var/www/www.test1.com' pattern='-d' => matched
(2) rewrite '/foo/!"#$%&'().txt' -> '/var/www/www.test1.com/foo/!"#$%&'().txt'
(2) local path result: /var/www/www.test1.com/foo/!"#$%&'().txt
(1) go-ahead with /var/www/www.test1.com/foo/!"#$%&'().txt [OK]
(3) [perdir /var/www/www.test1.com/] strip per-dir prefix: /var/www/www.test1.com/foo/!"#$%&'().txt -> foo/!"#$%&'().txt
(3) [perdir /var/www/www.test1.com/] applying pattern 'foo/(.*)' to uri 'foo/!"#$%&'().txt'
(2) [perdir /var/www/www.test1.com/] rewrite 'foo/!"#$%&'().txt' -> 'bar/!"#$%&'().txt'
(3) [perdir /var/www/www.test1.com/] add per-dir prefix: bar/!"#$%&'().txt -> /var/www/www.test1.com/bar/!"#$%&'().txt
(2) [perdir /var/www/www.test1.com/] strip document_root prefix: /var/www/www.test1.com/bar/!"#$%&'().txt -> /www.test1.com/bar/!"#$%&'().txt
(1) [perdir /var/www/www.test1.com/] internal redirect with /www.test1.com/bar/!"#$%&'().txt [INTERNAL REDIRECT]
(2) init rewrite engine with requested uri /www.test1.com/bar/!"
(3) applying pattern '.' to uri '/www.test1.com/bar/!"'
(4) RewriteCond: input='/var/www/www.test1.com' pattern='-d' => matched
(2) rewrite '/www.test1.com/bar/!"' -> '/var/www/www.test1.com/www.test1.com/bar/!"'
(2) local path result: /var/www/www.test1.com/www.test1.com/bar/!"
(1) go-ahead with /var/www/www.test1.com/www.test1.com/bar/!" [OK]
(3) [perdir /var/www/www.test1.com/] add path info postfix: /var/www/www.test1.com/www.test1.com -> /var/www/www.test1.com/www.test1.com/bar/!"
(3) [perdir /var/www/www.test1.com/] strip per-dir prefix: /var/www/www.test1.com/www.test1.com/bar/!" -> www.test1.com/bar/!"
(3) [perdir /var/www/www.test1.com/] applying pattern 'foo/(.*)' to uri 'www.test1.com/bar/!"'
(1) [perdir /var/www/www.test1.com/] pass through /var/www/www.test1.com/www.test1.com

特定の記号を含むファイル名へのリクエストはアンエスケープされ記号へ戻るが、internal redirectの際に再度エスケープされないまま渡されるため、0x23(#)以降は不要と判断され、完全に抜け落ちている

この問題を解決するために.htaccessへ
RewriteRule ^(.*)#(.*)$ $1\%23$2 [N]
と、邪悪で安易な方法だがともかく、追記してやりたいところだがこれは非常にマズい
internal redirectの際に抜け落ちこそしなくなるものの、再度このルールが評価されるため、ループしてしまうことになる

そしてこれと同等の現象だが、このような設定をすると
RewriteRule ^((?!foo/).*) foo/$1 [L]
(2) init rewrite engine with requested uri /index.html
(3) applying pattern '.' to uri '/index.html'
(4) RewriteCond: input='/var/www/www.test1.com' pattern='-d' => matched
(2) rewrite '/index.html' -> '/var/www/www.test1.com/index.html'
(2) local path result: /var/www/www.test1.com/index.html
(1) go-ahead with /var/www/www.test1.com/index.html [OK]
(3) [perdir /var/www/www.test1.com/] strip per-dir prefix: /var/www/www.test1.com/index.html -> index.html
(3) [perdir /var/www/www.test1.com/] applying pattern '^((?!foo/).*)' to uri 'index.html'
(2) [perdir /var/www/www.test1.com/] rewrite 'index.html' -> 'foo/index.html'
(3) [perdir /var/www/www.test1.com/] add per-dir prefix: foo/index.html -> /var/www/www.test1.com/foo/index.html
(2) [perdir /var/www/www.test1.com/] strip document_root prefix: /var/www/www.test1.com/foo/index.html -> /www.test1.com/foo/index.html
(1) [perdir /var/www/www.test1.com/] internal redirect with /www.test1.com/foo/index.html [INTERNAL REDIRECT]
(2) init rewrite engine with requested uri /www.test1.com/foo/index.html
(3) applying pattern '.' to uri '/www.test1.com/foo/index.html'
(4) RewriteCond: input='/var/www/www.test1.com' pattern='-d' => matched
(2) rewrite '/www.test1.com/foo/index.html' -> '/var/www/www.test1.com/www.test1.com/foo/index.html'
(2) local path result: /var/www/www.test1.com/www.test1.com/foo/index.html
(1) go-ahead with /var/www/www.test1.com/www.test1.com/foo/index.html [OK]
(3) [perdir /var/www/www.test1.com/] add path info postfix: /var/www/www.test1.com/www.test1.com -> /var/www/www.test1.com/www.test1.com/foo/index.html
(3) [perdir /var/www/www.test1.com/] strip per-dir prefix: /var/www/www.test1.com/www.test1.com/foo/index.html -> www.test1.com/foo/index.html
(3) [perdir /var/www/www.test1.com/] applying pattern '^((?!foo/).*)' to uri 'www.test1.com/foo/index.html'
(2) [perdir /var/www/www.test1.com/] rewrite 'www.test1.com/foo/index.html' -> 'foo/www.test1.com/foo/index.html'
(3) [perdir /var/www/www.test1.com/] add per-dir prefix: foo/www.test1.com/foo/index.html -> /var/www/www.test1.com/foo/www.test1.com/foo/index.html
(2) [perdir /var/www/www.test1.com/] strip document_root prefix: /var/www/www.test1.com/foo/www.test1.com/foo/index.html -> /www.test1.com/foo/www.test1.com/foo/index.html
(1) [perdir /var/www/www.test1.com/] internal redirect with /www.test1.com/foo/www.test1.com/foo/index.html [INTERNAL REDIRECT]
(2) init rewrite engine with requested uri /www.test1.com/foo/www.test1.com/foo/index.html
(3) applying pattern '.' to uri '/www.test1.com/foo/www.test1.com/foo/index.html'
(4) RewriteCond: input='/var/www/www.test1.com' pattern='-d' => matched
(2) rewrite '/www.test1.com/foo/www.test1.com/foo/index.html' -> '/var/www/www.test1.com/www.test1.com/foo/www.test1.com/foo/index.html'
(2) local path result: /var/www/www.test1.com/www.test1.com/foo/www.test1.com/foo/index.html
...

と、同様の理由から意図しないループが発生する

さて、これらの現象が発生する悪の根源はinternal redirectにあるが、仕様上どうしてもこれを回避することはできない
従って当然internal redirectを発生させるべきではなく、間違っても.htaccess(やDirectoryディレクティブ内)でmod_rewriteを走らせないのが最善策ということで落ち着くだろう

ただよく調べてみるとこの問題を回避する方法がないわけではないらしいので、もうちょっと粘ることにした

まず書いてあるようにREDIRECT_STATUSを見ればいいらしいので
<VirtualHost *:80>
DocumentRoot /var/www

RewriteEngine On
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule . - [S=100]
RewriteCond %{DOCUMENT_ROOT}/%{HTTP_HOST} -d
RewriteRule . %{DOCUMENT_ROOT}/%{HTTP_HOST}%{REQUEST_URI} [L]
RewriteRule . https://www.google.co.jp/search?q=%{HTTP_HOST}%{REQUEST_URI}
</VirtualHost>

と、した上で.htaccessは
RewriteEngine On
RewriteRule ^((?!foo/).*) foo/$1 [L]

と、する

このときの動作は
(2) init rewrite engine with requested uri /foo.html
(3) applying pattern '.' to uri '/foo.html'
(4) RewriteCond: input='' pattern='200' => not-matched
(3) applying pattern '.' to uri '/foo.html'
(4) RewriteCond: input='/var/www/www.test1.com' pattern='-d' => matched
(2) rewrite '/foo.html' -> '/var/www/www.test1.com/foo.html'
(2) local path result: /var/www/www.test1.com/foo.html
(1) go-ahead with /var/www/www.test1.com/foo.html [OK]
(3) [perdir /var/www/www.test1.com/] strip per-dir prefix: /var/www/www.test1.com/foo.html -> foo.html
(3) [perdir /var/www/www.test1.com/] applying pattern '^((?!foo/).*)' to uri 'foo.html'
(2) [perdir /var/www/www.test1.com/] rewrite 'foo.html' -> 'foo/foo.html'
(3) [perdir /var/www/www.test1.com/] add per-dir prefix: foo/foo.html -> /var/www/www.test1.com/foo/foo.html
(2) [perdir /var/www/www.test1.com/] strip document_root prefix: /var/www/www.test1.com/foo/foo.html -> /www.test1.com/foo/foo.html
(1) [perdir /var/www/www.test1.com/] internal redirect with /www.test1.com/foo/foo.html [INTERNAL REDIRECT]
(2) init rewrite engine with requested uri /www.test1.com/foo/foo.html
(3) applying pattern '.' to uri '/www.test1.com/foo/foo.html'
(4) RewriteCond: input='200' pattern='200' => matched
(1) pass through /www.test1.com/foo/foo.html
(3) [perdir /var/www/www.test1.com/] strip per-dir prefix: /var/www/www.test1.com/foo/foo.html -> foo/foo.html
(3) [perdir /var/www/www.test1.com/] applying pattern '^((?!foo/).*)' to uri 'foo/foo.html'
(1) [perdir /var/www/www.test1.com/] pass through /var/www/www.test1.com/foo/foo.html

このような結果となり、意図した挙動を示す
internal-redirectは発生してはいるものの、問題は回避されている

0x23(#)以降が抜け落ちる問題は以前解決していないものの、.htaccessにも
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule . - [S=100]
RewriteRule ^(.*)#(.*)$ $1\%23$2 [N]

と、加えることでループを回避しつつ問題を解決できる
ただこれは0x23(#)の話でしかない

と、いうのは
(2) init rewrite engine with requested uri /!"#$%&'().txt
(3) applying pattern '.' to uri '/!"#$%&'().txt'
(4) RewriteCond: input='' pattern='200' => not-matched
(3) applying pattern '.' to uri '/!"#$%&'().txt'
(4) RewriteCond: input='/var/www/www.test1.com' pattern='-d' => matched
(2) rewrite '/!"#$%&'().txt' -> '/var/www/www.test1.com/!"#$%&'().txt'
(2) local path result: /var/www/www.test1.com/!"#$%&'().txt
(1) go-ahead with /var/www/www.test1.com/!"#$%&'().txt [OK]
(3) [perdir /var/www/www.test1.com/] strip per-dir prefix: /var/www/www.test1.com/!"#$%&'().txt -> !"#$%&'().txt
(3) [perdir /var/www/www.test1.com/] applying pattern '.' to uri '!"#$%&'().txt'
(4) [perdir /var/www/www.test1.com/] RewriteCond: input='' pattern='200' => not-matched
(3) [perdir /var/www/www.test1.com/] strip per-dir prefix: /var/www/www.test1.com/!"#$%&'().txt -> !"#$%&'().txt
(3) [perdir /var/www/www.test1.com/] applying pattern '^(.*)#(.*)$' to uri '!"#$%&'().txt'
(2) [perdir /var/www/www.test1.com/] rewrite '!"#$%&'().txt' -> '!"%23$%&'().txt'
(3) [perdir /var/www/www.test1.com/] add per-dir prefix: !"%23$%&'().txt -> /var/www/www.test1.com/!"%23$%&'().txt
(3) [perdir /var/www/www.test1.com/] strip per-dir prefix: /var/www/www.test1.com/!"%23$%&'().txt -> !"%23$%&'().txt
(3) [perdir /var/www/www.test1.com/] applying pattern '.' to uri '!"%23$%&'().txt'
(4) [perdir /var/www/www.test1.com/] RewriteCond: input='' pattern='200' => not-matched
(3) [perdir /var/www/www.test1.com/] strip per-dir prefix: /var/www/www.test1.com/!"%23$%&'().txt -> !"%23$%&'().txt
(3) [perdir /var/www/www.test1.com/] applying pattern '^(.*)#(.*)$' to uri '!"%23$%&'().txt'
(3) [perdir /var/www/www.test1.com/] strip per-dir prefix: /var/www/www.test1.com/!"%23$%&'().txt -> !"%23$%&'().txt
(3) [perdir /var/www/www.test1.com/] applying pattern '^((?!foo/).*)' to uri '!"%23$%&'().txt'
(2) [perdir /var/www/www.test1.com/] rewrite '!"%23$%&'().txt' -> 'foo/!"%23$%&'().txt'
(3) [perdir /var/www/www.test1.com/] add per-dir prefix: foo/!"%23$%&'().txt -> /var/www/www.test1.com/foo/!"%23$%&'().txt
(2) [perdir /var/www/www.test1.com/] strip document_root prefix: /var/www/www.test1.com/foo/!"%23$%&'().txt -> /www.test1.com/foo/!"%23$%&'().txt
(1) [perdir /var/www/www.test1.com/] internal redirect with /www.test1.com/foo/!"%23$%&'().txt [INTERNAL REDIRECT]

で、400が返されログの出力はここで止まる
これは今まで抜け落ちていた0x25(%)が起因となるもので、0x26(&)が続く0x25(%)が不正と判断されて発生している

この問題はhttpd.confへ
RewriteMap escape int:escape
と、した上で.htaccessへ
RewriteRule ^(.*)$ ${escape:$1}
と、することで回避できる

余談だがこの内容を書くにあたって2日要しており、ここまでは昨日の話

実は記号問題は重要視していなかった
これは真面目に調べていなかったということと同意義で、実はこんな面倒なことは必要ない
と、いうのも前述したとおり、そもそも.htaccessでmod_rewrite使うべきではない、ということで結論が出てしまっていた(し、それで運用することにした)からだ

ところが調べてみるとBフラグというものが存在していて、これは
RewriteRule ^(.*)$ $1 [B]
と、するだけでよく
(2) init rewrite engine with requested uri /!"#$%&'().txt
(3) applying pattern '.' to uri '/!"#$%&'().txt'
(4) RewriteCond: input='' pattern='200' => not-matched
(3) applying pattern '.' to uri '/!"#$%&'().txt'
(4) RewriteCond: input='/var/www/www.test1.com' pattern='-d' => matched
(2) rewrite '/!"#$%&'().txt' -> '/var/www/www.test1.com/!"#$%&'().txt'
(2) local path result: /var/www/www.test1.com/!"#$%&'().txt
(1) go-ahead with /var/www/www.test1.com/!"#$%&'().txt [OK]
(3) [perdir /var/www/www.test1.com/] strip per-dir prefix: /var/www/www.test1.com/!"#$%&'().txt -> !"#$%&'().txt
(3) [perdir /var/www/www.test1.com/] applying pattern '.' to uri '!"#$%&'().txt'
(4) [perdir /var/www/www.test1.com/] RewriteCond: input='' pattern='200' => not-matched
(3) [perdir /var/www/www.test1.com/] strip per-dir prefix: /var/www/www.test1.com/!"#$%&'().txt -> !"#$%&'().txt
(3) [perdir /var/www/www.test1.com/] applying pattern '^(.*)$' to uri '!"#$%&'().txt'
(5) [perdir /var/www/www.test1.com/] escaping backreference '!"#$%&'().txt' to '%21%22%23%24%25%26%27%28%29%2etxt'
(2) [perdir /var/www/www.test1.com/] rewrite '!"#$%&'().txt' -> '%21%22%23%24%25%26%27%28%29%2etxt'
(3) [perdir /var/www/www.test1.com/] add per-dir prefix: %21%22%23%24%25%26%27%28%29%2etxt -> /var/www/www.test1.com/%21%22%23%24%25%26%27%28%29%2etxt
(3) [perdir /var/www/www.test1.com/] strip per-dir prefix: /var/www/www.test1.com/%21%22%23%24%25%26%27%28%29%2etxt -> %21%22%23%24%25%26%27%28%29%2etxt
(3) [perdir /var/www/www.test1.com/] applying pattern '^((?!foo/).*)' to uri '%21%22%23%24%25%26%27%28%29%2etxt'
(2) [perdir /var/www/www.test1.com/] rewrite '%21%22%23%24%25%26%27%28%29%2etxt' -> 'foo/%21%22%23%24%25%26%27%28%29%2etxt'
(3) [perdir /var/www/www.test1.com/] add per-dir prefix: foo/%21%22%23%24%25%26%27%28%29%2etxt -> /var/www/www.test1.com/foo/%21%22%23%24%25%26%27%28%29%2etxt
(2) [perdir /var/www/www.test1.com/] strip document_root prefix: /var/www/www.test1.com/foo/%21%22%23%24%25%26%27%28%29%2etxt -> /www.test1.com/foo/%21%22%23%24%25%26%27%28%29%2etxt
(1) [perdir /var/www/www.test1.com/] internal redirect with /www.test1.com/foo/%21%22%23%24%25%26%27%28%29%2etxt [INTERNAL REDIRECT]
(2) init rewrite engine with requested uri /www.test1.com/foo/!"#$%&'().txt
(3) applying pattern '.' to uri '/www.test1.com/foo/!"#$%&'().txt'
(4) RewriteCond: input='200' pattern='200' => matched
(1) pass through /www.test1.com/foo/!"#$%&'().txt
(3) [perdir /var/www/www.test1.com/] strip per-dir prefix: /var/www/www.test1.com/foo/!"#$%&'().txt -> foo/!"#$%&'().txt
(3) [perdir /var/www/www.test1.com/] applying pattern '.' to uri 'foo/!"#$%&'().txt'
(4) [perdir /var/www/www.test1.com/] RewriteCond: input='200' pattern='200' => matched
(1) [perdir /var/www/www.test1.com/] pass through /var/www/www.test1.com/foo/!"#$%&'().txt

このように問題を回避できる

さて、この内容を書くための検証環境は最終的に
RewriteEngine On
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule . - [S=100]
RewriteCond %{DOCUMENT_ROOT}/%{HTTP_HOST} -d
RewriteRule . %{DOCUMENT_ROOT}/%{HTTP_HOST}%{REQUEST_URI} [L]
RewriteRule . https://www.google.co.jp/search?q=%{HTTP_HOST}%{REQUEST_URI}


RewriteEngine On
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule . - [S=100]
RewriteRule ^(.*)$ $1 [B]
RewriteRule ^((?!foo/).*) foo/$1 [L]

このような設定で動作している

運用上障害になり得る問題は解決しているが、実は1つ踏んだままの地雷がある
それはmod_autoindexを使った場合で、/へのリクエストを/www.test1.com/foo/だと勘違いしてしまう
確かにmod_rewriteによって/へのリクエストは/www.test1.com/foo/になるのだが、mod_autoindexで生成されたページでIndex of /www.test1.com/fooと表示されることを期待はしていないし、Parent Directoryのリンクが見えて、そのリンク先が/www.test1.com/になるのは困る

これは解決できていない
しいて言えば前述しているように.htaccessでmod_rewriteを使わなければ再現しない
add comment ( 3462 views )

<<First <Back | 1 | 2 | 3 | 4 | Next> Last>>