web-dev-qa-db-ja.com

mod_rewriteとApacheを使用して特殊文字をエンコードする方法は?

タグ付けシステム用のきれいなURLとすべての特殊文字が必要です:+&#%、および=。リンクを二重にエンコードすることなくmod_rewriteでこれを行う方法はありますか?

Delicious.comとstackoverflowは、単独でエンコードされた特殊文字を処理できるようだと思います。魔法の式は何ですか?

私がやりたいことの例は次のとおりです。

http://www.foo.com/tag/c%2b%2b

次のRewriteRuleをトリガーします。

RewriteRule ^tag/(.*)   script.php?tag=$1

タグの値は「c ++」になります

Apache/mod_rewriteの通常の操作は、プラス記号をスペースに変換するように見えるため、このようには機能しません。プラス記号を '%252B'にダブルエンコードすると、目的の結果が得られます-ただし、URLが乱雑になり、かなりハッキングされているように見えます。

28
Aldie

Apache/mod_rewriteの通常の操作は、プラス記号をスペースに変換するように見えるため、このようには機能しません。

私はそれが全く起こっているとは思わない。 Apacheは、パス部分で%2Bを+ sにデコードしています。これは、+が有効な文字であるためです。 mod_rewriteがリクエストを見る前にこれを行います。

そのため、mod_rewriteはリクエスト「/ tag/c ++」を「script.php?tag = c ++」に変更します。ただし、application/x-www-form-encoded形式のクエリ文字列コンポーネントでは、エスケープ規則はパス部分に適用される規則とはわずかに異なります。特に、「+」はスペースの省略形です(「%20」と同じようにエンコードできますが、これは今では変更できない古い動作です)。

そのため、PHPのフォーム読み取りコードは 'c ++'を受け取り、それをC-space-spaceとして_GETにダンプします。

これを回避する方法は、書き換えフラグ「B」を使用することです。 http://httpd.Apache.org/docs/2.2/mod/mod_rewrite.html#rewriteflags を参照してください-奇妙なことに、多かれ少なかれ同じ例を使用しています!

RewriteRule ^tag/(.*)$ /script.php?tag=$1 [B]
26
bobince

私はあなたが何を求めているのか理解していないが、ApacheのNEディレクティブのRewriteRule(noescape)フラグは興味があるかもしれない。基本的に、_mod_rewrite_が、指定した置換パターンの特殊文字を自動的にエスケープしないようにします。 Apache 2.2ドキュメントに記載されている例は次のとおりです。

_RewriteRule /foo/(.*) /bar/arg=P1\%3d$1 [R,NE]
_

これにより、たとえば_/foo/zed_が_/bar/arg=P1%3dzed_へのリダイレクトに変換され、スクリプト_/bar_がargという名前のクエリパラメーターと値_P1=zed_、_PATH_INFO_を見ると(大丈夫、それはrealクエリパラメータではないので、私を訴えます;-P)。

少なくとも、私はそれがどのように機能するかと思います。 。 。私はその特定のフラグを自分で使用したことはありません。

5
David Z

Urlに+サインを付けたmod_rewriteでも同様の問題に遭遇します。以下のようなシナリオ:

_http://deskdomain/2013/08/09/a+b+c.html_のような書き換えが必要な+記号付きのURLがあります

RewriteRule ^/(.*) http://mobiledomain/do/urlRedirect?url=http://%{HTTP_Host}/$1

StrutsアクションurlRedirectはurlパラメーターを取得し、何らかの変更を行い、そのURLを別のリダイレクトに使用します。ただし、req.getParameter( "url")では、+記号が空に変わり、パラメーターurlの内容が_http://deskdomain/2013/08/09/a b c.html_になり、リダイレクト404が見つかりません。それを解決するために(事前の回答からヘルプを得る)、書き換えフラグB(エスケープ逆参照)、およびNE(エスケープなし)を使用します。

RewriteRule ^/(.*) http://mobiledomain/do/urlRedirect?url=http://%{HTTP_Host}/$1 [B,NE]

Bは+から%2Bにエスケープし、NEはmod_writeエスケープ%2Bから%252B(二重エスケープ+記号)を防ぐため、req.getParameter("url")=http://deskdomain/2013/08/09/a+b+c.html

その理由は、req.getParameter( "url")がエスケープを解除し、+記号が空にならないことが原因だと思います。エスケープ解除%2Bを1回+に試してから、エスケープ解除+をもう一度空にできます。

_"%2B" unescape-> "+" unescape-> " "_

1
yren

根本的な問題は、1つのエンコード(具体的にはプラス記号はプラス記号)を持つリクエストから、異なるエンコード(プラス記号はスペースを表す)を持つリクエストに移行することです。解決策は、mod_rewriteが行うデコードをバイパスし、パスを生のリクエストからクエリ文字列に直接変換することです。

書き換えルールの通常のフローをバイパスするには、生のリクエスト文字列を環境変数に直接ロードし、通常の書き換えパスの代わりに環境変数を変更します。既にエンコードされているため、クエリ文字列に移動するときにエンコードを心配する必要は通常ありません。ただし、必要なのは、スペースではなくプラス記号として適切に中継されるように、プラス記号をパーセントエンコードすることです。

ルールは非常にシンプルです:

RewriteEngine On

RewriteRule ^script.php$ - [L]

# Move the path from the raw request into _rq
RewriteCond %{ENV:_rq} =""
RewriteCond %{THE_REQUEST} "^[^ ]+ (/path/[^/]+/[^? ]+)"
RewriteRule .* - [E=_rq:%1]

# encode the plus signs (%2B)  (Loop with [N])
RewriteCond %{ENV:_rq} "/path/([^/]+)/(.*)\+(.*)$"
RewriteRule .* - [E=_rq:/path/%1/%2\%2B%3,N]

# finally, move it from the path to the query string
# ([NE] says to not re-code it)
RewriteCond %{ENV:_rq} "/path/([^/]+)/(.*)$"
RewriteRule .* /path/script.php?%1=%2 [NE]

この些細なscript.phpは、動作することを確認します。

<input readonly type="text" value="<?php echo $_GET['tag']; ?>" />
1
danorton

RewriteMapを使用してようやく機能するようになりました。

Httpd.confファイルRewriteMap es int:escapeにエスケープマップを追加しました

書き換えルールで使用しました

RewriteRule ([^?.]*) /abc?arg1=${es:$1}&country_sniff=true [L]
1
Nitin