[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;
 		}
 	}
 }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.