web-dev-qa-db-ja.com

nginx real_ip_headerとX-Forwarded-Forが間違っているようです

HTTPヘッダーX-Forwarded-Forのウィキペディアの説明は次のとおりです。

X-Forwarded-For:client1、proxy1、proxy2、...

ディレクティブreal_ip_headerのnginxドキュメンテーションは、一部を読み取ります:

このディレクティブは、置換IPアドレスの転送に使用されるヘッダーの名前を設定します。
X-Forwarded-Forの場合、このモジュールはX-Forwarded-Forヘッダーのlast ipを使用して置換します。 【エンファシス鉱山】

これらの2つの説明は互いに矛盾しているように見えます。このシナリオでは、X-Forwarded-Forヘッダーは説明どおりです-クライアントの「実際の」IPアドレスが左端のエントリです。同様に、nginxの動作はright-most値を使用することです。これは明らかに、プロキシサーバーの1つにすぎません。

X-Real-IPについての私の理解は、仮定実際のクライアントIPアドレスを決定するために使用される-プロキシではありません。私は何かが足りないのですか、これはnginxのバグですか?

さらに、X-Real-IPの定義で示されているように、X-Forwarded-Forヘッダーにleft-most値を表示させる方法についての提案はありますか?

62
Kirk Woll

複数のIPがチェーンされている場合にX-Forwarded-Forの問題を解決するための鍵は、最近導入された構成オプションreal_ip_recursive(nginx 1.2.1および1.3.0で追加)であると思います。 nginx realip docs から:

再帰検索が有効になっている場合、信頼できるアドレスの1つと一致する元のクライアントアドレスは、リクエストヘッダーフィールドで送信された最後の信頼できないアドレスに置き換えられます。

nginxは、信頼されていると想定された唯一のIPアドレスだったため、デフォルトでチェーンの最後のIPアドレスを取得していました。ただし、新しいreal_ip_recursiveを有効にし、複数のset_real_ip_fromオプションを使用すると、複数の信頼できるプロキシを定義でき、最後の信頼できないIPをフェッチします。

たとえば、次の構成の場合:

set_real_ip_from 127.0.0.1;
set_real_ip_from 192.168.2.1;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

そしてX-Forwarded-Forヘッダーは次のようになります:

X-Forwarded-For: 123.123.123.123, 192.168.2.1, 127.0.0.1

nginxは、クライアントのIPアドレスとして123.123.123.123を選択します。

Nginxが左端のIPアドレスを選択するのではなく、信頼できるプロキシを明示的に定義する必要がある理由は、簡単なIPスプーフィングを防ぐためです。

クライアントの実際のIPアドレスが123.123.123.123であるとしましょう。また、クライアントの状態が悪く、IPアドレスが11.11.11.11になりすまそうとしているとします。彼らはすでにこのヘッダーが配置されているサーバーにリクエストを送信します:

X-Forwarded-For: 11.11.11.11

リバースプロキシはこのX-Forwarded-ForチェーンにIPを追加するだけなので、nginxが到達すると次のようになるとしましょう。

X-Forwarded-For: 11.11.11.11, 123.123.123.123, 192.168.2.1, 127.0.0.1

左端のアドレスを取得しただけであれば、クライアントはIPアドレスを簡単に偽装できます。ただし、上記の例のnginx構成では、nginxはプロキシとして最後の2つのアドレスのみを信頼します。つまり、スプーフィングされたIPが実際に一番左にあるにもかかわらず、nginxは123.123.123.123をIPアドレスとして正しく選択します。

105
Nick M

X-Forwarded-Forヘッダーの解析は、nginx real_ipモジュールに実際に欠陥があります。

len = r->headers_in.x_forwarded_for->value.len;
ip = r->headers_in.x_forwarded_for->value.data;

for (p = ip + len - 1; p > ip; p--) {
  if (*p == ' ' || *p == ',') {
    p++;
    len -= p - ip;
    ip = p;
    break;
  }
}

ヘッダー文字列の右端から始まり、スペースまたはカンマが見つかるとすぐに検索を停止し、IP変数のスペースまたはカンマの右側にパーツを貼り付けます。したがって、最新プロキシアドレスを元のクライアントアドレスとして扱います。

仕様によるとニースではありません。これは、RFCで苦痛に明白な用語で綴られていないことの危険です。

余談:Squidによって最初に定義された形式の適切なプライマリソースを見つけることさえ困難です- ドキュメント は注文を確認します。左端は元のクライアント、右端は最新の追加です。そのウィキペディアのページに[引用が必要]を追加したくてたまらない。 1つ anonymous edit は、この件に関するインターネットの権限のようです。

可能であれば、中間プロキシがヘッダーの最後に自分自身を追加するのを止めて、実際のクライアントアドレスのみを残すことができますか?

9
Shane Madden

X-Real-IPは、サーバーが通信している実際のクライアント(サーバーの「実際の」クライアント)のIPアドレスであり、プロキシ接続の場合はプロキシサーバーです。これが、X-Real-IPがX-Forwarded-Forヘッダーの最後のIPを含む理由です。

5
user558061

答えより警告の方が多い...

私のメインサーバー(データソース)が既にローカルで実行されているNginxキャッシュサーバーの背後にあることを認識せずに、複数のマップの場所にいくつかのNginxキャッシュサーバーを追加しようとしました 、ローカルサーバーがApacheを実行するように構成されている場合があり、Nginxがその前に置かれてキャッシュとして機能します。

2つ目の別のNginxキャッシュサーバーを追加すると、キャッシュサーバーが2つになるため、HTTP_X_REAL_IPまたはHTTP_X_FORWARDED_FORが正しく表示されず、訪問者のIPではなくサーバーのIPが1つ表示されます。

私の場合、新しいキャッシュサーバーを設定して、ソース/メインサーバーのローカルNginxキャッシュをバイパスし、Apacheポート(私の場合はポート7080)から直接データを取得するように設定しました。

別の解決策は、ローカルキャッシュサーバーでHTTP_X_REAL_IPを削除することです。

ところでPleskパネルを実行していました。

0
adrianTNT