-
-
Notifications
You must be signed in to change notification settings - Fork 758
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add search for a trusted host in ProxyHeadersMiddleware #591
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,17 +8,28 @@ | |
|
||
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#Proxies | ||
""" | ||
from typing import List | ||
|
||
|
||
class ProxyHeadersMiddleware: | ||
def __init__(self, app, trusted_hosts="127.0.0.1"): | ||
self.app = app | ||
if isinstance(trusted_hosts, str): | ||
self.trusted_hosts = [item.strip() for item in trusted_hosts.split(",")] | ||
self.trusted_hosts = {item.strip() for item in trusted_hosts.split(",")} | ||
else: | ||
self.trusted_hosts = trusted_hosts | ||
self.trusted_hosts = set(trusted_hosts) | ||
self.always_trust = "*" in self.trusted_hosts | ||
|
||
def get_trusted_client_host( | ||
self, x_forwarded_for_hosts | ||
): # type: (List[str]) -> str | ||
if self.always_trust: | ||
return x_forwarded_for_hosts[0] | ||
|
||
for host in reversed(x_forwarded_for_hosts): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's just a security feature: we should return only the last non-trusted host. Trivial case:
So in this scenario, a header will be Unfortunately, IRL we have a situation when we have a chain of proxies, and we can trust only a few of them, e.g:
In this case, non-trusted proxy-server B can replace the real client address, so we should return the first non-trusted host and it will be tl;dr: In a simple case, we can trust all of the proxies with |
||
if host not in self.trusted_hosts: | ||
return host | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need a fall through case so there's no way we can end up returning There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I fail to see how it would be possible There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would happen if all the IPs in the chain were explicitly listed in In this case, is there any other justifiable return value than |
||
|
||
async def __call__(self, scope, receive, send): | ||
if scope["type"] in ("http", "websocket"): | ||
client_addr = scope.get("client") | ||
|
@@ -38,7 +49,10 @@ async def __call__(self, scope, receive, send): | |
# X-Forwarded-For header. We've lost the connecting client's port | ||
# information by now, so only include the host. | ||
x_forwarded_for = headers[b"x-forwarded-for"].decode("latin1") | ||
host = x_forwarded_for.split(",")[-1].strip() | ||
x_forwarded_for_hosts = [ | ||
item.strip() for item in x_forwarded_for.split(",") | ||
] | ||
host = self.get_trusted_client_host(x_forwarded_for_hosts) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what if the x-forwarded-for format is IP1:PORT1,IP2:PORT2,which is common in the L7 load balance? the port here is always set zero which is not reasonable. |
||
port = 0 | ||
scope["client"] = (host, port) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For speedup
str not in collection
operations