[interchange] Enhance TrustProxy to handle multiple chained proxies
Jon Jensen <[email protected]>
| Newsgroups | gmane.comp.web.interchange.cvs |
|---|---|
| Message-ID | <[email protected]> |
commit 29c73e67feb9240bca3a60288cad7d7293f482c9 Author: Jon Jensen <[email protected]> Date: Thu Mar 31 22:24:07 2011 -0500 Enhance TrustProxy to handle multiple chained proxies This can happen if, for example, you have a first proxy at 10.10.10.1 which proxies to 10.10.10.2 which then hits your web server that passes control to Interchange. If you visit from 192.168.1.1, Interchange will see this HTTP header: X-Forwarded-For: 192.168.1.1, 10.10.10.1 and the request will have the source IP address 10.10.10.2. But if you set this in interchange.cfg: TrustProxy 10.10.10.1, 10.10.10.2 # order irrelevant then Interchange will see past the two trusted proxies and set its standard variable $CGI::remote_addr to 192.168.1.1, so that the customer's IP address gets used. lib/Vend/Server.pm | 47 +++++++++++++++++++++++++++++------------------ 1 files changed, 29 insertions(+), 18 deletions(-) --- diff --git a/lib/Vend/Server.pm b/lib/Vend/Server.pm index 7305cec..a6620f5 100644 --- a/lib/Vend/Server.pm +++ b/lib/Vend/Server.pm @@ -127,26 +127,37 @@ sub populate { # try to get originating host's IP address if request was # forwarded through a trusted proxy - my $ip; - if ($Global::TrustProxy - and ($CGI::remote_addr =~ $Global::TrustProxy - or $CGI::remote_host =~ $Global::TrustProxy) - and $ip = $cgivar->{HTTP_X_FORWARDED_FOR}) { - # trust only the last hop's IP address before our trusted proxy - # when multiples are present in a comma-separated list - $ip =~ s/.*,//; - $ip =~ s/^\s+//; $ip =~ s/\s+$//; - if ($ip =~ /^\d\d?\d?\.\d\d?\d?\.\d\d?\d?\.\d\d?\d?$/) { + if ( + $Global::TrustProxy + and ( + $CGI::remote_addr =~ $Global::TrustProxy + or $CGI::remote_host =~ $Global::TrustProxy + ) + and my $forwarded_for = $cgivar->{HTTP_X_FORWARDED_FOR} + ) { + # multiple source IP addresses may appear in X-Forwarded-For header + # in a comma-separated list + for my $ip (reverse grep /\S/, split /\s*,\s*/, $forwarded_for) { + # do we have a valid-looking IP address? + if ($ip !~ /^\d\d?\d?\.\d\d?\d?\.\d\d?\d?\.\d\d?\d?$/) { + # if not, log error and ignore X-Forwarded-For header + ::logGlobal( + { level => 'info' }, + "Unknown X-Forwarded-For header set from trusted proxy %s: %s", + $CGI::remote_addr, + $forwarded_for, + ); + last; + } + + # skip any other upstream trusted proxies + next if $ip =~ $Global::TrustProxy; + + # rightmost IP address that's not a trusted proxy is the customer IP + # address as far as we're concerned, so keep that and exit loop $CGI::remote_addr = $ip; undef $CGI::remote_host; - } - else { - ::logGlobal( - { level => 'info' }, - "Unknown HTTP_X_FORWARDED_FOR header set from trusted proxy %s: '%s'", - $CGI::remote_addr, - $cgivar->{HTTP_X_FORWARDED_FOR}, - ); + last; } } }