Only use the X-Forwarded-For header if connection is from a trusted network#15182
Only use the X-Forwarded-For header if connection is from a trusted network#15182balloob merged 1 commit intohome-assistant:devfrom colinodell:fix-untrusted-x-forwarded-for
Conversation
| X_FORWARDED_FOR in request.headers and | ||
| any(connected_ip in trusted_network | ||
| for trusted_network in trusted_networks) | ||
| ): |
There was a problem hiding this comment.
closing bracket does not match visual indentation
| @middleware | ||
| async def real_ip_middleware(request, handler): | ||
| """Real IP middleware.""" | ||
| connected_ip = ip_address(request.transport.get_extra_info('peername')[0]) |
There was a problem hiding this comment.
line too long (82 > 79 characters)
|
This seems to work locally for me, but I'd strongly prefer if somebody else could also test to confirm this works as expected. |
|
Doesn't this PR trying to do the same as #14379? |
|
I believe that #14379 is still vulnerable to spoofing because it never checks which IP the connection is being made from, unlike this PR. For example, given this configuration: http:
api_password: YOUR_PASSWORD
use_x_forwarded_for: True
trusted_networks:
- 127.0.0.1#14379 would still allow IP spoofing as long as the malicious header contains a trusted network like That approach does indeed prevent someone from gaining access by guessing a trusted network IP, but the IP spoofing still allows them to bypass rate limiting or perform a DoS attack. |
|
To clarify the difference further:
|
|
IIUC, |
|
HA password authentication should still work, as that is a separate middleware which runs after we've determined the user's real IP. When a proxied request comes in from a trusted proxy, this middleware will use the There are only two scenarios I can envision where authentication would unexpectedly be bypassed: Scenario 1 - MisconfigurationThe user has added the proxy IP to Scenario 2 - Real IP and proxy IP are identicalIf (for some reason) the HTTP requests originates from the same IP as the proxy server, then yes authentication will be bypassed. For example, let's say you're running HA on your local machine with this configuration: http:
api_password: YOUR_PASSWORD
use_x_forwarded_for: True
trusted_networks:
- 127.0.0.1And let's say you've also got nginx running locally as your proxy. Well, nginx is going to send an |
|
I can submit a follow-up PR to add a separate |
Thanks for clarifying, I didn't realize the auth middleware ran after. I hope there is a test for that since it seems like it would be easy to regress and accidentally bypass authentication for all users who use a proxy. |
* Document X-Forwarded-For behavior changes Documentation for home-assistant/core#15182 * Update http.markdown * ✏️ Language tweaks
|
@mnoorenberghe I have opened #15204 which adds a separate whitelist for proxies.
Because the two features would use two completely different lists if/when #15204 is merged, I don't think that test would be required as that edge case scenario would no longer be possible :) |
|
@colinodell thanks for trying to address this issue. I don't have this setup myself so can't check, but I did a code review of the changes. Also, having the proxy in the trusted list might be undesirable, as the trusted list is basically a "password not required list" for the purpose of authentication. A separate "trusted_proxies" will help with that. To handle spoofing you need to take the last IP address in the forwarded_for chain from the header when a trusted proxy (in a general term) is used. This is as simple as adding a "reverse()" call. Another solution was proposed by @mvn23 at #14379 . It ignores any IPs not in the trusted_networks. That PR is not accepted at this time, so I believe we are still vulnerable. I think debugging and resolving this would be easier if you would have proper automated tests. I will prepare a list of use cases and the expected REAL_IP and post here and in #14345 . |
|
@ayavilevich I've submitted #15204 which should address both of your concerns |
|
@colinodell that looks good. I have added a list of suggested test cases. Please take a look |
Description:
As noted in #14345, Home Assistant blindly trusts the
X-Forwarded-Forheader without checking who sent it or if they should be trusted. This allows anyone to spoof their IP address during authentication ifhttp.use_x_forwarded_foris enabled which can lead to some major security problems: #14345 (comment)This PR changes the behavior of
real_ip_middlewareto only use theX-Forwarded-Forheader if the incoming connection is from a trusted IP address (per thetrusted_networkssetting).Related issue (if applicable): fixes #14345
Pull request in home-assistant.github.io with documentation (if applicable): home-assistant/home-assistant.io#5618
Example entry for
configuration.yaml(if applicable):Assuming that a reverse proxy is running on the same machine, here's how you'd configure Home Assistant to detect the true IP address of the end-user:
Checklist:
tox. Your PR cannot be merged unless tests passIf user exposed functionality or configuration variables are added/changed:
If the code does not interact with devices: