# プロキシ構成
リバースプロキシサーバー(nginxなど)を使用する場合、request.ip
の値にはプロキシのIP(通常は127.0.0.1
が含まれます。ほとんどの場合、これはあなたが望むものではありません。
Sanicは、request.remote_addr
として利用可能な真のクライアントIPを決定するためにプロキシヘッダーを使用するように構成できます。完全な外部URLは、ヘッダーフィールド_if available_からも構築されます。
::: ヒント 注意 適切な予防措置がなければ、悪意のあるクライアントはプロキシヘッダーを使用して独自のIPを偽装することができます。このような問題を回避するために、Sanicは明示的に有効になっていない限り、プロキシヘッダーを使用しません。 :::
リバースプロキシの背後にあるサービスは、次の構成値の1つ以上を設定する必要があります。
FORWARDED_SECRET
REAL_IP_HEADER
PROXIES_COUNT
app.config.FORWARDED_SECRET = "super-duper-secret"
app.config.REAL_IP_HEADER = "CF-Connecting-IP"
app.config.PROXIES_COUNT = 2
# 転送されたヘッダー
Forwarded
ヘッダーを使用するには、信頼できるプロキシサーバーに知られている値にapp.config.FORWARDED_SECRET
を設定する必要があります。この秘密は、特定のプロキシサーバーを安全に識別するために使用されます。
29
30
Sanicは秘密鍵のない要素を無視し、秘密が設定されていない場合、ヘッダーを解析することさえしません。
他のすべてのプロキシヘッダーは、クライアントに関する完全な情報をすでに持っているため、信頼できる転送された要素が見つかると無視されます。
Forwarded
ヘッダーの詳細については、関連するMDN (opens new window)およびNginx (opens new window)の記事をお読みください。
# 従来のプロキシヘッダー
# IP-Header
プロキシが既知のヘッダーのIPアドレスを転送すると、「REAL_IP_HEADER」設定値でそれが何であるかをSanicに伝えることができます。
# X-Forwarded-For
このヘッダーには、通常、プロキシの各レイヤーを介したIPアドレスのチェーンが含まれています。PROXIES_COUNT
を設定すると、クライアントの実際のIPアドレスを取得する深さがSanicに指示されます。この値は、チェーン内のIPアドレスの_expected_数に等しいはずです。
# Other X-headers
クライアントIPが次のいずれかの方法で見つかった場合、SanicはURL部分に次のヘッダーを使用します。
- x-forwarded-proto
- x-forwarded-host
- x-forwarded-port
- x-forwarded-path
- x-scheme
# 例えば
次の例では、すべての要求はエンドポイントが次のようになります。
@app.route("/fwd")
async def forwarded(request):
return json(
{
"remote_addr": request.remote_addr,
"scheme": request.scheme,
"server_name": request.server_name,
"server_port": request.server_port,
"forwarded": request.forwarded,
}
)
# 例えば 1
FORWARDED_SECRETが設定されていない場合、xヘッダーは尊重されるべきです
app.config.PROXIES_COUNT = 1
app.config.REAL_IP_HEADER = "x-real-ip"
$ curl localhost:8000/fwd \
-H 'Forwarded: for=1.1.1.1, for=injected;host=", for="[::2]";proto=https;host=me.tld;path="/app/";secret=mySecret,for=broken;;secret=b0rked, for=127.0.0.3;scheme=http;port=1234' \
-H "X-Real-IP: 127.0.0.2" \
-H "X-Forwarded-For: 127.0.1.1" \
-H "X-Scheme: ws" \
-H "Host: local.site" | jq
# curl response
{
"remote_addr": "127.0.0.2",
"scheme": "ws",
"server_name": "local.site",
"server_port": 80,
"forwarded": {
"for": "127.0.0.2",
"proto": "ws"
}
}
# 例えば 2
FORWARDED_SECRETが設定されました
app.config.PROXIES_COUNT = 1
app.config.REAL_IP_HEADER = "x-real-ip"
app.config.FORWARDED_SECRET = "mySecret"
$ curl localhost:8000/fwd \
-H 'Forwarded: for=1.1.1.1, for=injected;host=", for="[::2]";proto=https;host=me.tld;path="/app/";secret=mySecret,for=broken;;secret=b0rked, for=127.0.0.3;scheme=http;port=1234' \
-H "X-Real-IP: 127.0.0.2" \
-H "X-Forwarded-For: 127.0.1.1" \
-H "X-Scheme: ws" \
-H "Host: local.site" | jq
# curl response
{
"remote_addr": "[::2]",
"scheme": "https",
"server_name": "me.tld",
"server_port": 443,
"forwarded": {
"for": "[::2]",
"proto": "https",
"host": "me.tld",
"path": "/app/",
"secret": "mySecret"
}
}
# 例えば 3
空の転送ヘッダー - > Xヘッダーを使用する
app.config.PROXIES_COUNT = 1
app.config.REAL_IP_HEADER = "x-real-ip"
app.config.FORWARDED_SECRET = "mySecret"
$ curl localhost:8000/fwd \
-H "X-Real-IP: 127.0.0.2" \
-H "X-Forwarded-For: 127.0.1.1" \
-H "X-Scheme: ws" \
-H "Host: local.site" | jq
# curl response
{
"remote_addr": "127.0.0.2",
"scheme": "ws",
"server_name": "local.site",
"server_port": 80,
"forwarded": {
"for": "127.0.0.2",
"proto": "ws"
}
}
# Example 4
Header present but not matching anything
app.config.PROXIES_COUNT = 1
app.config.REAL_IP_HEADER = "x-real-ip"
app.config.FORWARDED_SECRET = "mySecret"
$ curl localhost:8000/fwd \
-H "Forwarded: nomatch" | jq
# curl response
{
"remote_addr": "",
"scheme": "http",
"server_name": "localhost",
"server_port": 8000,
"forwarded": {}
}
# Example 5
Forwarded header present but no matching secret -> use X-headers
app.config.PROXIES_COUNT = 1
app.config.REAL_IP_HEADER = "x-real-ip"
app.config.FORWARDED_SECRET = "mySecret"
$ curl localhost:8000/fwd \
-H "Forwarded: for=1.1.1.1;secret=x, for=127.0.0.1" \
-H "X-Real-IP: 127.0.0.2" | jq
# curl response
{
"remote_addr": "127.0.0.2",
"scheme": "http",
"server_name": "localhost",
"server_port": 8000,
"forwarded": {
"for": "127.0.0.2"
}
}
# Example 6
Different formatting and hitting both ends of the header
app.config.PROXIES_COUNT = 1
app.config.REAL_IP_HEADER = "x-real-ip"
app.config.FORWARDED_SECRET = "mySecret"
$ curl localhost:8000/fwd \
-H 'Forwarded: Secret="mySecret";For=127.0.0.4;Port=1234' | jq
# curl response
{
"remote_addr": "127.0.0.4",
"scheme": "http",
"server_name": "localhost",
"server_port": 1234,
"forwarded": {
"secret": "mySecret",
"for": "127.0.0.4",
"port": 1234
}
}
# Example 7
Test escapes (modify this if you see anyone implementing quoted-pairs)
app.config.PROXIES_COUNT = 1
app.config.REAL_IP_HEADER = "x-real-ip"
app.config.FORWARDED_SECRET = "mySecret"
$ curl localhost:8000/fwd \
-H 'Forwarded: for=test;quoted="\,x=x;y=\";secret=mySecret' | jq
# curl response
{
"remote_addr": "test",
"scheme": "http",
"server_name": "localhost",
"server_port": 8000,
"forwarded": {
"for": "test",
"quoted": "\\,x=x;y=\\",
"secret": "mySecret"
}
}
# Example 8
Secret insulated by malformed field #1
app.config.PROXIES_COUNT = 1
app.config.REAL_IP_HEADER = "x-real-ip"
app.config.FORWARDED_SECRET = "mySecret"
$ curl localhost:8000/fwd \
-H 'Forwarded: for=test;secret=mySecret;b0rked;proto=wss;' | jq
# curl response
{
"remote_addr": "test",
"scheme": "http",
"server_name": "localhost",
"server_port": 8000,
"forwarded": {
"for": "test",
"secret": "mySecret"
}
}
# Example 9
Secret insulated by malformed field #2
app.config.PROXIES_COUNT = 1
app.config.REAL_IP_HEADER = "x-real-ip"
app.config.FORWARDED_SECRET = "mySecret"
$ curl localhost:8000/fwd \
-H 'Forwarded: for=test;b0rked;secret=mySecret;proto=wss' | jq
# curl response
{
"remote_addr": "",
"scheme": "wss",
"server_name": "localhost",
"server_port": 8000,
"forwarded": {
"secret": "mySecret",
"proto": "wss"
}
}
# Example 10
Unexpected termination should not lose existing acceptable values
app.config.PROXIES_COUNT = 1
app.config.REAL_IP_HEADER = "x-real-ip"
app.config.FORWARDED_SECRET = "mySecret"
$ curl localhost:8000/fwd \
-H 'Forwarded: b0rked;secret=mySecret;proto=wss' | jq
# curl response
{
"remote_addr": "",
"scheme": "wss",
"server_name": "localhost",
"server_port": 8000,
"forwarded": {
"secret": "mySecret",
"proto": "wss"
}
}
# Example 11
Field normalization
app.config.PROXIES_COUNT = 1
app.config.REAL_IP_HEADER = "x-real-ip"
app.config.FORWARDED_SECRET = "mySecret"
$ curl localhost:8000/fwd \
-H 'Forwarded: PROTO=WSS;BY="CAFE::8000";FOR=unknown;PORT=X;HOST="A:2";PATH="/With%20Spaces%22Quoted%22/sanicApp?key=val";SECRET=mySecret' | jq
# curl response
{
"remote_addr": "",
"scheme": "wss",
"server_name": "a",
"server_port": 2,
"forwarded": {
"proto": "wss",
"by": "[cafe::8000]",
"host": "a:2",
"path": "/With Spaces\"Quoted\"/sanicApp?key=val",
"secret": "mySecret"
}
}
# Example 12
Using "by" field as secret
app.config.PROXIES_COUNT = 1
app.config.REAL_IP_HEADER = "x-real-ip"
app.config.FORWARDED_SECRET = "_proxySecret"
$ curl localhost:8000/fwd \
-H 'Forwarded: for=1.2.3.4; by=_proxySecret' | jq
# curl response
{
"remote_addr": "1.2.3.4",
"scheme": "http",
"server_name": "localhost",
"server_port": 8000,
"forwarded": {
"for": "1.2.3.4",
"by": "_proxySecret"
}
}