Skip to content

Commit

Permalink
VIP Request Block: Add function to allow for blocking by partial User…
Browse files Browse the repository at this point in the history
… Agent string. (#4001)

* Add function to allow for blocking by partial User Agent string.

* Add test for blocking by partial User Agent string match

* Use strpos() over str_contains for PHP 7.x backwards-compatibility

* Fix linting

* Use late static binding

Co-authored-by: Rebecca Hum <16962021+rebeccahum@users.noreply.github.com>
  • Loading branch information
mslinnea and rebeccahum committed Jan 6, 2023
1 parent b6fd950 commit 90bd992
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 5 deletions.
23 changes: 19 additions & 4 deletions lib/class-vip-request-block.php
Expand Up @@ -50,15 +50,15 @@ public static function ip( string $value ) {
$hdr = strtolower( $_SERVER['HTTP_TRUE_CLIENT_IP'] );
$bin = inet_pton( $hdr );
if ( $bin === $ip || $hdr === $value ) {
return self::block_and_log( $value, 'true-client-ip' );
return static::block_and_log( $value, 'true-client-ip' );
}
}

if ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
$ips = array_map( fn ( string $s ): string => strtolower( trim( $s ) ), explode( ',', $_SERVER['HTTP_X_FORWARDED_FOR'] ) );
$bin = array_map( 'inet_pton', $ips );
if ( in_array( $value, $ips, true ) || in_array( $ip, $bin, true ) ) {
return self::block_and_log( $value, 'x-forwarded-for' );
return static::block_and_log( $value, 'x-forwarded-for' );
}
}

Expand All @@ -75,7 +75,22 @@ public static function ip( string $value ) {
public static function ua( string $user_agent ) {
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPressVIPMinimum.Variables.RestrictedVariables.cache_constraints___SERVER__HTTP_USER_AGENT__
if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && $user_agent === $_SERVER['HTTP_USER_AGENT'] ) {
return self::block_and_log( $user_agent, 'user-agent' );
return static::block_and_log( $user_agent, 'user-agent' );
}

return false;
}

/**
* Block by partial match of the user agent header
*
* @param string $user_agent_substring target user agent to be blocked.
* @return void|bool
*/
public static function ua_partial_match( string $user_agent_substring ) {
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPressVIPMinimum.Variables.RestrictedVariables.cache_constraints___SERVER__HTTP_USER_AGENT__, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && false !== strpos( $_SERVER['HTTP_USER_AGENT'], $user_agent_substring ) ) {
return static::block_and_log( $user_agent_substring, 'user-agent' );
}

return false;
Expand All @@ -95,7 +110,7 @@ public static function header( string $header, string $value ) {
$key = sprintf( 'HTTP_%s', str_ireplace( 'HTTP_', '', $key ) );

if ( isset( $_SERVER[ $key ] ) && $value === $_SERVER[ $key ] ) {
return self::block_and_log( $value, $header );
return static::block_and_log( $value, $header );
}

return false;
Expand Down
15 changes: 14 additions & 1 deletion tests/lib/test-vip-request-block.php
Expand Up @@ -11,7 +11,8 @@ class VIP_Request_Block_Test extends WP_UnitTestCase {
*/

public function tearDown(): void {
unset( $_SERVER['HTTP_TRUE_CLIENT_IP'], $_SERVER['HTTP_X_FORWARDED_FOR'] );
// phpcs:ignore WordPressVIPMinimum.Variables.RestrictedVariables.cache_constraints___SERVER__HTTP_USER_AGENT__
unset( $_SERVER['HTTP_TRUE_CLIENT_IP'], $_SERVER['HTTP_X_FORWARDED_FOR'], $_SERVER['HTTP_USER_AGENT'] );
parent::tearDown();
}

Expand Down Expand Up @@ -78,4 +79,16 @@ public function data_ipv6_corner_cases(): iterable {
[ 'HTTP_X_FORWARDED_FOR', '2001:4860:4860:0:0:0:0:8844', '2001:4860:4860:0000:0000:0000:0000:8844' ],
];
}

public function test_ua_partial_match() {
// Test that a partial match of the user agent string blocks bad site.
$_SERVER['HTTP_USER_AGENT'] = 'WordPress/6.1.1; https://www.BadSite.com'; // phpcs:ignore WordPressVIPMinimum.Variables.RestrictedVariables.cache_constraints___SERVER__HTTP_USER_AGENT__
$actual = VIP_Request_Block::ua_partial_match( 'https://www.BadSite.com' );
self::assertTrue( $actual, 'Expected request to be blocked based on partial User Agent string match.' );

// Test that allowed user agent string is not blocked.
$_SERVER['HTTP_USER_AGENT'] = 'WordPress/6.1.1; https://www.example.com'; // phpcs:ignore WordPressVIPMinimum.Variables.RestrictedVariables.cache_constraints___SERVER__HTTP_USER_AGENT__
$actual = VIP_Request_Block::ua_partial_match( 'https://www.BadSite.com' );
self::assertFalse( $actual, 'Expected request to be allowed.' );
}
}

0 comments on commit 90bd992

Please sign in to comment.