Skip to content

[2.1] Address issues with proxy lookups#9229

Open
sbulen wants to merge 2 commits into
SimpleMachines:release-2.1from
sbulen:21_proxy_ips
Open

[2.1] Address issues with proxy lookups#9229
sbulen wants to merge 2 commits into
SimpleMachines:release-2.1from
sbulen:21_proxy_ips

Conversation

@sbulen

@sbulen sbulen commented May 12, 2026

Copy link
Copy Markdown
Contributor

Fixes #9143

I took a stab at this.

Changes:

  • Improved logic for determining user IP vs proxy server IP.
  • Quite a bit stricter - doesn't touch ANYTHING unless everything validates correctly. Solves initial problem where bogus data was passed as IPs. Very short leash on 'auto' setting, no longer the default if unspecified...
  • If valid proxy servers are not specified in proxy_ip_servers, proxy server should be local/private.
  • Properly uses FIRST ip in header list, not last, for user IP, per RFC 7239.
  • Validate IPs via filter_var(), not via regex.
  • Consistent support & logic across ipv4 & ipv6.
  • Bug fix: IPs passed via headers weren't validated...
  • Bug fix: Fixed explode issue, where it would fail if there weren't spaces after commas...
  • Bug fix: Fixed CIDR checks - invalid regex pattern for cidr, caused everything to return positive...
  • Bug fix: Loop logic issues addressed... After exploding $_SERVER[$proxyIPheader], it kept operating on the whole thing instead of each ip...
  • Bug fix: Localhost checks were... illogical... E.g., they didn't factor in whether we'd already validated the proxy server vs proxy_ip_servers.
  • In short, I don't think the old logic worked at all. There were @todo's in there noting outstanding ipv6 work was needed. This was a 'nuke it from orbit and start over' scenario; it needed simplifying.

CONCERN:
Note that the ban check validates against BOTH the member_ip and member_ip2, i.e., both the proxy and the end user IP. Also note that the proxy may be using a valid 'localhost' IP. So... When we get this working, a ban on a user by IP can effectively ban a valid 'localhost' IP, i.e., a huge swath of (or even all...) proxy traffic.

Here:

// Check both IP addresses.

I believe we should only check the end user in the ban check, not the proxy server...

If agreed, I'll add this to this PR...

If we wish to proceed with this I'll submit the 3.0 version once approved.

@sbulen sbulen marked this pull request as draft May 12, 2026 00:37
@sbulen

sbulen commented May 12, 2026

Copy link
Copy Markdown
Contributor Author

I'm still doing testing... (I tested as best I can without actually having a proxy!)

I think we have an issue where the editor config is out of sync with the edit checks??? It's not liking my EOF, and it's not letting me fix it either...

Signed-off-by: Shawn Bulen <bulens@pacbell.net>
Signed-off-by: Shawn Bulen <bulens@pacbell.net>
@sbulen sbulen marked this pull request as ready for review May 12, 2026 17:00
@sbulen

sbulen commented May 12, 2026

Copy link
Copy Markdown
Contributor Author

Ready for review/test.

My core tests are below:

=======================================

	//Test 1: Plain user ipv4, no config
	$_SERVER['REMOTE_ADDR'] = '173.228.74.9';
	$modSettings['proxy_ip_header'] = 'autodetect';
	$modSettings['proxy_ip_servers'] = '';

	// Expected result:
	$_SERVER['REMOTE_ADDR'] = '173.228.74.9';
	$_SERVER['BAN_CHECK_IP'] = '173.228.74.9';


=======================================

	//Test 2: Plain user ipv6, no config
	$_SERVER['REMOTE_ADDR'] = '2001:5a8:4284:ed00:8007:6463:700f:f07b';
	$modSettings['proxy_ip_header'] = 'autodetect';
	$modSettings['proxy_ip_servers'] = '';

	// Expected result:
	$_SERVER['REMOTE_ADDR'] = '2001:5a8:4284:ed00:8007:6463:700f:f07b';
	$_SERVER['BAN_CHECK_IP'] = '2001:5a8:4284:ed00:8007:6463:700f:f07b';

=======================================

	//Test 3: Plain ipv6, HTTP_X_FORWARDED_FOR (should use local)
	$_SERVER['REMOTE_ADDR'] = 'fe80::';
	$_SERVER['HTTP_X_FORWARDED_FOR'] = '2001:5a8:4284:ed00:8007:6463:700f:f07b, 2405:8100::,2405:b500::';
	$modSettings['proxy_ip_header'] = 'HTTP_X_FORWARDED_FOR';
	$modSettings['proxy_ip_servers'] = '';

	// Expected result:
	$_SERVER['REMOTE_ADDR'] = '2001:5a8:4284:ed00:8007:6463:700f:f07b';
	$_SERVER['BAN_CHECK_IP'] = 'fe80::';

=======================================

	//Test 4: ipv4, CF
	$_SERVER['REMOTE_ADDR'] = '103.31.6.1';
	$_SERVER['HTTP_CF_CONNECTING_IP'] = '173.228.74.9,2405:8100::,2405:b500::';
	$modSettings['proxy_ip_header'] = 'HTTP_CF_CONNECTING_IP';
	$modSettings['proxy_ip_servers'] = '173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22,2400:cb00::/32,2606:4700::/32,2803:f800::/32,2405:b500::/32,2405:8100::/32,2a06:98c0::/29,2c0f:f248::/32';

	// Expected result:
	$_SERVER['REMOTE_ADDR'] = '173.228.74.9';
	$_SERVER['BAN_CHECK_IP'] = '103.31.6.1';

=======================================

	//Test 5: ipv6, CF
	$_SERVER['REMOTE_ADDR'] = '2c0f:f248::aaaa:111';
	$_SERVER['HTTP_CF_CONNECTING_IP'] = '2001:5a8:4284:ed00:8007:6463:700f:f07b,2405:8100::,2405:b500::';
	$modSettings['proxy_ip_header'] = 'HTTP_CF_CONNECTING_IP';
	$modSettings['proxy_ip_servers'] = '173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22,2400:cb00::/32,2606:4700::/32,2803:f800::/32,2405:b500::/32,2405:8100::/32,2a06:98c0::/29,2c0f:f248::/32';

	// Expected result:
	$_SERVER['REMOTE_ADDR'] = '2001:5a8:4284:ed00:8007:6463:700f:f07b';
	$_SERVER['BAN_CHECK_IP'] = '2c0f:f248::aaaa:111';

=======================================

	//Test 6: ipv6 ::ffff
	$_SERVER['REMOTE_ADDR'] = '::ffff:173.228.74.9';
	$modSettings['proxy_ip_header'] = 'autodetect';
	$modSettings['proxy_ip_servers'] = '';

	// Expected result:
	$_SERVER['REMOTE_ADDR'] = '173.228.74.9';
	$_SERVER['BAN_CHECK_IP'] = '173.228.74.9';

=======================================

	//Test 7: ipv6 with brackets ::ffff
	$_SERVER['REMOTE_ADDR'] = '2405:b500::4';
	$_SERVER['HTTP_CF_CONNECTING_IP'] = '[::ffff:173.228.74.9],2405:8100::,2405:b500::';
	$modSettings['proxy_ip_header'] = 'autodetect';
	$modSettings['proxy_ip_servers'] = '[2405:b500::1], [2405:b500::3], [2405:b500::4]';

	// Expected result:
	$_SERVER['REMOTE_ADDR'] = '173.228.74.9';
	$_SERVER['BAN_CHECK_IP'] = '2405:b500::4';

=======================================

	//Test 8: ipv4, CF, whitespaces & tabs
	$_SERVER['REMOTE_ADDR'] = '103.31.7.255';
	$_SERVER['HTTP_CF_CONNECTING_IP'] = '173.228.74.9, 2405:8100:: , 	2405:b500::		';
	$modSettings['proxy_ip_header'] = 'HTTP_CF_CONNECTING_IP';
	$modSettings['proxy_ip_servers'] = '173.245.48.0/20,	103.21.244.0/22,103.22.200.0/22,	103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22,2400:cb00::/32,2606:4700::/32,2803:f800::/32,2405:b500::/32,2405:8100::/32,2a06:98c0::/29,	2c0f:f248::/32';

	// Expected result:
	$_SERVER['REMOTE_ADDR'] = '173.228.74.9';
	$_SERVER['BAN_CHECK_IP'] = '103.31.7.255';

=======================================

	//Test 9: ipv6, CF, whitespaces & tabs
	$_SERVER['REMOTE_ADDR'] = '2C0F:F248:FFFF:FFFF:FFFF:FFFF:FFFF:FFFE';
	$_SERVER['HTTP_CF_CONNECTING_IP'] = '2001:5a8:4284:ed00:8007:6463:700f:f07b   , 2405:8100::, 2405:b500::';
	$modSettings['proxy_ip_header'] = 'HTTP_CF_CONNECTING_IP';
	$modSettings['proxy_ip_servers'] = '173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22,2400:cb00::/32,2606:4700::/32,2803:f800::/32,2405:b500::/32,  2405:8100::/32,  2a06:98c0::/29,  2c0f:f248::/32  ';

	// Expected result:
	$_SERVER['REMOTE_ADDR'] = '2001:5a8:4284:ed00:8007:6463:700f:f07b';
	$_SERVER['BAN_CHECK_IP'] = '2C0F:F248:FFFF:FFFF:FFFF:FFFF:FFFF:FFFE';

=======================================

	//Test 10: ipv6, CF, wrong header specified
	$_SERVER['REMOTE_ADDR'] = '2c0f:f248::';
	$_SERVER['HTTP_CF_CONNECTING_IP'] = '2001:5a8:4284:ed00:8007:6463:700f:f07b   , 2405:8100::, 2405:b500::';
	$modSettings['proxy_ip_header'] = 'HTTP_X_FORWARDED_FOR';
	$modSettings['proxy_ip_servers'] = '173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22,2400:cb00::/32,2606:4700::/32,2803:f800::/32,2405:b500::/32,  2405:8100::/32,  2a06:98c0::/29,  2c0f:f248::/32  ';

	// Expected result:
	$_SERVER['REMOTE_ADDR'] = '2c0f:f248::';
	$_SERVER['BAN_CHECK_IP'] = '2c0f:f248::';

=======================================

	//Test 11: ipv6, CF, JUNK kiddie scripter....
	$_SERVER['REMOTE_ADDR'] = '2c0f:f248::';
	$_SERVER['HTTP_CF_CONNECTING_IP'] = 'why   <>;[;]];];[;)\<\>(."<">", oh why,  would I ever';
	$modSettings['proxy_ip_header'] = 'HTTP_CF_CONNECTING_IP';
	$modSettings['proxy_ip_servers'] = '173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22,2400:cb00::/32,2606:4700::/32,2803:f800::/32,2405:b500::/32,  2405:8100::/32,  2a06:98c0::/29,  2c0f:f248::/32  ';

	// Expected result:
	$_SERVER['REMOTE_ADDR'] = '2c0f:f248::';
	$_SERVER['BAN_CHECK_IP'] = '2c0f:f248::';

=======================================

	//Test 12: ipv4, CF, invalid proxy addr
	$_SERVER['REMOTE_ADDR'] = '104.31.6.1';
	$_SERVER['HTTP_CF_CONNECTING_IP'] = '173.228.74.9,2405:8100::,2405:b500::';
	$modSettings['proxy_ip_header'] = 'HTTP_CF_CONNECTING_IP';
	$modSettings['proxy_ip_servers'] = '173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22,2400:cb00::/32,2606:4700::/32,2803:f800::/32,2405:b500::/32,2405:8100::/32,2a06:98c0::/29,2c0f:f248::/32';

	// Expected result:
	$_SERVER['REMOTE_ADDR'] = '104.31.6.1';
	$_SERVER['BAN_CHECK_IP'] = '104.31.6.1';

=======================================

	//Test 13: ipv6, CF, invalid proxy addr
	$_SERVER['REMOTE_ADDR'] = '2ddd:f248::aaaa:111';
	$_SERVER['HTTP_CF_CONNECTING_IP'] = '2001:5a8:4284:ed00:8007:6463:700f:f07b,2405:8100::,2405:b500::';
	$modSettings['proxy_ip_header'] = 'HTTP_CF_CONNECTING_IP';
	$modSettings['proxy_ip_servers'] = '173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22,2400:cb00::/32,2606:4700::/32,2803:f800::/32,2405:b500::/32,2405:8100::/32,2a06:98c0::/29,2c0f:f248::/32';

	// Expected result:
	$_SERVER['REMOTE_ADDR'] = '2ddd:f248::aaaa:111';
	$_SERVER['BAN_CHECK_IP'] = '2ddd:f248::aaaa:111';

=======================================

@sbulen

sbulen commented May 13, 2026

Copy link
Copy Markdown
Contributor Author

Note that the old CIDR lookup algorithm works... I'd never seen it done nibble-by-nibble before like that.

I still prefer the rewrite:

  • Can be passed IPs as well as CIDRs
  • No preg_split bug
  • binary safe comparisons
  • Doesn't try to find IPv6s within ipv4 CIDRs, etc.

@jdarwood007

Copy link
Copy Markdown
Member

Can we get the 3.0 equivalent.

I also really hate that we are creating new functions, but for simplify_ip we can't avoid it due to repetition. Unless we use an Anonymous function. Mostly because then we have to introduce that into 3.0 with backwards compatibility function

@sbulen

sbulen commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

OK, I'll work on the 3.0 version. May take me a few days.

I'll also figure out the backwards compatibility stuff in that PR.

I'd really like it if someone could test this - from behind a real proxy. I had to emulate things...

@jdarwood007

Copy link
Copy Markdown
Member

Its possible we can avoid the backwards compatibility needs. One of the 2 functions is only called once, so we could just inline its check. The other function we can anonymize it and assign it to a variable in the function, like $simplify_ip. Since its inside the logic, I wouldn't see a reason we would have to do any backwards compatibility calls. If we publish it with the change, then for 3.0 to have true compatibility with 2.1 mods, we have to introduce logic itno the Subs-Compat to be able to handle a call to that function.

@sbulen

sbulen commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Note we also need to address the CONCERN noted above...

Once this is working, ban checks will ban whole proxy servers. We need to align on the fix for that before this is merged.

(We've been lucky this logic hasn't worked...)

@jdarwood007

Copy link
Copy Markdown
Member

Any solutions you recommend?

@sbulen

sbulen commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

I believe we should only check the end user in the ban check, not the proxy server...

Remove the proxy ip from the ban query.

@jdarwood007

Copy link
Copy Markdown
Member

Sounds reasonable for 2.1.

In reality, it should mean the ban check IP (actually the proxy ip) only actually contains a proxy IP. A poorly configured SMF server though would allow people through if you expected to ban their proxy IP

Perhaps for 3.0 we can improve this by ensuring that when a new ban is added we check to make sure its not a proxy server IP. Of course updating the proxy server IPs wouldn't trigger a recheck of all bans. This would ensure that new proxy server IPs could be filtered if the admin needs to. On a separate note, we should change references from ban check ip to proxy server ip in 3.0, this should be a separate PR.

@sbulen

sbulen commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

Yep. A helpful edit when adding bans is a good idea.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants