web-dev-qa-db-ja.com

nginx $ uriはURLデコードされます

nginxをリバースプロキシとして使用しており、次のような2つのルールがあります。

location ~ ^/indirect {
  rewrite ^/indirect(.*) /foobar$1;
}

location ~ ^/foobar {
  set $url http://example.com/something/index.php?var1=hello&access=$scheme://$Host$uri;
  proxy_pass $url;
}

ご覧のとおり、プロキシされたページに$uri変数をパラメータとして渡しています($uri変数はnginx変数です。 http core module を参照してください)。 =ドキュメント)。

問題は、http://example.com/foobar/hello%20worldにアクセスすると、$uri変数に/foobar/hello worldが含まれていることです(ご覧のとおり、%20はURLデコードされた値であるスペースに置き換えられています)。そして、nginxは、proxy_pass行を実行する前にhttpステータスコード400(不正なリクエスト)を返します(そこに接続されているバックエンドはありません)。

変数$request_uriも使用できます。これは、クライアントによって発行された元の要求URIを保持するため、この場合は%20シーケンスで正しい値を保持します。しかし、クライアントが/indirectパスを通過する場合、$request_uriには/indirect/...が含まれるため、これを使用することはできませんが、バックエンドに渡されるaccessパラメータは常に/foobar/...

複数のindirectに似たルールがあります(これはDAV/calDAV/cardDAVサーバー用であり、複数のパスに接続する複数のクライアントがそこにあるため、これらのindirectに似たルールが必要です)、そこでproxy_passを実行することは現実的ではなく、/foobarパスに直接アクセスするクライアントがあります。

それで、URLデコードせずに$uriを取得する方法はありますか?

受け入れられない可能性のあること:

  • リクエストをクライアントから送信される可能性があるため、リクエストをダブルエンコードして送信する
  • メンテナンスの問題が発生するため、最終的なURLをeacy間接ルールと「直接」ルールで複数回指定する。
7

nginx/1.2.1では、%20の問題を再現できませんでした。スペースにデコードすると、nginx内で 400 Bad Request が発生します。おそらくそれは上流から来ているのでしょうか?

とにかく、実際に rewrite ディレクティブを介して提供される有限状態オートマトンを使用して、デコードされた要求が$uriに含まれないようにしながらも、リクエストのあらゆる種類の変換。

https://stackoverflow.com/questions/28684300/nginx-pass-proxy-subdirectory-without-url-decoding/37584637#37584637

アイデアは、$uriを適切に変更しても、再デコードされないことです。そして、ご存知のように、デコードされていないものは$request_uriに既にあります。あとは、一方を他方に設定し、それを1日と呼ぶだけです。

server {
    listen 2012;
    location /a {
        rewrite ^/a(.*) /f$1 last;
    }
    location /i {
        rewrite ^ $request_uri;
        rewrite ^/i(.*) /f$1 last;
        return 400; #if the second rewrite won't match
    }
    location /f {
        set $url http://127.0.0.1:2016/s?v=h&a=$scheme://$Host$uri;
        proxy_pass $url;
    }
}

server {
    listen 2016;
    return 200 $request_uri\n;
}

そして、はい、上のrewrite ^ $request_uri;の部分でうまくいきます:

% echo localhost:2012/{a,i,f}/h%20w | xargs -n1 curl
/s?v=h&a=http://localhost/f/h w
/s?v=h&a=http://localhost/f/h%20w
/s?v=h&a=http://localhost/f/h w
%

(「直接」のものもデコードされないようにしたい場合は、「間接」にすることもおそらく最も簡単です。)

3
cnst

私が見つけた唯一の方法は、 HttpSetMiscModule を使用することです:

location ~
 ^/indirect {
  set_escape_uri $key $1;
  rewrite ^/indirect(.*) /foobar$key;
}

location ~ ^/foobar {
  set_escape_uri $key $uri;
  set $url http://example.com/something/index.php?var1=hello&access=$scheme://$Host$key;
  proxy_pass $url;
}

誰かがより良い方法を知っている場合(ルートアクセス権がないため、外部モジュールでnginxをコンパイルする必要はありません)、私に知らせてください!

1
stackular