Accept cookies for analytics, social media, and advertising, or learn more and adjust your preferences. These cookies are on by default for visitors outside the UK and EEA. Privacy Notice.
Forwarded
ヘッダの使用Forwarded
ヘッダの使用¶従来、HTTPリバースプロキシは非標準ヘッダを使って、ユーザのIPアドレスと他のリクエストプロパティについてアップストリームサーバに通知します。
X-Forwarded-For: 12.34.56.78, 23.45.67.89
X-Real-IP: 12.34.56.78
X-Forwarded-Host: example.com
X-Forwarded-Proto: https
NGINXは$proxy_add_x_forwarded_for 変数を提供して 到着するX-Forwarded-For
ヘッダに$remote_addrを自動的に追加します。
RFC 7239 は新しい Forwarded
ヘッダを標準化して、この情報をもっと体系化された方法で伝送します:
Forwarded: for=12.34.56.78;host=example.com;proto=https, for=23.45.67.89
Forwarded
の主な利点は拡張性です。例えば、X-Forwarded-For
を使うと、“最後から2番目のIPアドレスを取得するが、リクエストが 10.0.0.0/8 から来た場合のみ” などのハードコードされた規則が無いと、どれが信頼するIPアドレスか知ることができません。一方で、Forwarded
を使うと、信頼できるフロントエンド プロキシは自分自身を識別するための秘密トークンを含めることができます。
Forwarded: for=12.34.56.78, for=23.45.67.89;secret=egah2CGj55fSJFs, for=10.1.2.3
NGINXは$proxy_add_x_forwarded_for
のように$proxy_add_forwarded
変数を提供しませんが、configで部分的に エミュレートすることができます:
map $remote_addr $proxy_forwarded_elem {
# IPv4 addresses can be sent as-is
~^[0-9.]+$ "for=$remote_addr";
# IPv6 addresses need to be bracketed and quoted
~^[0-9A-Fa-f:.]+$ "for=\"[$remote_addr]\"";
# Unix domain socket names cannot be represented in RFC 7239 syntax
default "for=unknown";
}
map $http_forwarded $proxy_add_forwarded {
# If the incoming Forwarded header is syntactically valid, append to it
"~^(,[ \\t]*)*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*([ \\t]*,([ \\t]*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*)?)*$" "$http_forwarded, $proxy_forwarded_elem";
# Otherwise, replace it
default "$proxy_forwarded_elem";
}
次に、proxy_pass
ブロックで、次のように書くことができます:
proxy_set_header Forwarded $proxy_add_forwarded;
そこに追加のパラメータを追加することもできます:
proxy_set_header Forwarded "$proxy_add_forwarded;proto=$scheme";
警告
ticket #1316 が解決されるまで、この解決法は複数の到着するForwarded
ヘッダをサポートしません。例えば、リクエストが次のものを持つ場合:
Forwarded: for=1.2.3.4
Forwarded: for=5.6.7.8
上で定義された$proxy_add_forwarded
は以下を生成します:
Forwarded: for=1.2.3.4, for=9.10.11.12
これは、複数の到着するヘッダを正しくjoinする$proxy_add_x_forwarded_for
とは対照的です。
上記のmap $http_forwarded
内の巨大な regexは、構文的に有効な全てのForwarded
ヘッダと一致します。これにより、NGINXが誤った形式のヘッダを盲目的に追加しないようにします。そうでなければ、外部の攻撃者が以下のような何かを送信できます:
Forwarded: for=injected;by="
そして、NGINXは次のものを生成します:
Forwarded: for=injected;by=", for=real
アップストリーム サーバがこのようなForwarded
を解析する方法に応じて、for=real
要素が表示される場合とされない場合があります。(X-Forwarded-For
とは異なり、カンマは引用符で囲まれた有効な文字列で発生する可能性があるため、カンマで分割することができません)。
アップストリームがこれを正しく処理することが分っている場合、構文チェックを落として無効な要素を渡すことができます。これは相互運用性、アップストリーム ログなどに役立つことがあります:
map $http_forwarded $proxy_add_forwarded {
"" "$proxy_forwarded_elem";
default "$http_forwarded, $proxy_forwarded_elem";
}
X-Forwarded-*
との共存¶上記の解決法は、従来のX-Forwarded-*
ヘッダを新しいForwarded
形式に “アップグレード” することができません。状況に応じて、おそらく次のいずれかを引き継ぐ必要があります:
proxy_set_header Forwarded $proxy_add_forwarded;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
あるいは、アップストリームを混乱させないために、それらを積極的に削除します:
proxy_set_header Forwarded $proxy_add_forwarded;
proxy_set_header X-Forwarded-For "";