Currently, the handling of the X-Forwarded-For header in the ProxyHeaders middleware is overly simplistic and naive. It simply chooses the leftmost value.
However, this opens up two potential issues.
- It is valid and reasonable usage for the X-Forwarded-For chain to include client-side proxy values, in which the leftmost value could be a local ip address. This will nearly never be the desired value for consumers of this package.
An example of this might be:
X-Forwarded-For: 192.168.1.23, 42.34.21.211
Where 192.168.1.23
represents a private, client-side IP address, and 42.34.21.211
represents a client-side http proxy server. In most real world cases, the desired value for the "real" IP would be the proxy server, 42.34.21.211.
- For use of this package directly behind an AWS ELB, the AWS ELB does not purge the previously set X-Forwarded-For value. This is actually a good thing, because some requests may require passes through multiple ELB layers and we need a way to see the correct original address. However, that opens up the possibility for users to include their own X-Forwarded-For header that provides a fabricated IP source as the leftmost value.
It appears that these issues are known and the suggestion is to use this only behind a reverse proxy which purges and resets the X-Forwarded-For header. This cripples the application of this middleware in real world usage. For example, it cannot be reliably or safely used directly behind an Amazon ELB. In the case of nginx, it requires a specific configuration to be safely used. Further, many users of this library are likely to use this middleware in an environment where the X-Forwared-For header may contain untrusted values, without understanding the specific vulnerabilities that this opens up.
I would suggest a more comprehensive handling of the X-Forwarded-For header that is similar to the nginx implementation of set_real_ip_from. That is, that there should be a defined set of trusted CIDRs, and that the real address is the last address in the chain that is not within the list of trusted CIDRs. If all IP values fall within the list of trusted CIDRs, then the leftmost value would be used.
I would further suggest a default set of trusted CIDR values based on the IETF private address spaces, which is a more secure starting point and would require specific changes to introduce vulnerabilities.
Given that implementation, the limitation on the secure application of this code is simply that it has to exist behind a trusted proxy that properly sets or appends to the X-Forwarded-For header.
Before I work on code for such an implementation, I would like to know if the maintainers and community are open to such a change.