VYPR
High severityNVD Advisory· Published Dec 22, 2022· Updated Apr 15, 2025

CodeIgniter is vulnerable to IP address spoofing when using proxy

CVE-2022-23556

Description

CodeIgniter is a PHP full-stack web framework. This vulnerability may allow attackers to spoof their IP address when the server is behind a reverse proxy. This issue has been patched, please upgrade to version 4.2.11 or later, and configure Config\App::$proxyIPs. As a workaround, do not use $request->getIPAddress().

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

CodeIgniter 4 before 4.2.11 allows IP address spoofing when behind a reverse proxy due to improper validation of proxy headers.

Vulnerability

Overview

CodeIgniter 4's getIPAddress() method in the RequestTrait class trusts HTTP headers such as X-Forwarded-For without verifying that the connecting proxy is a trusted proxy. When Config\App::$proxyIPs is configured, the method accepts the first IP from the header chain without validating the source, allowing an attacker to spoof their IP address by sending a crafted header [1][2].

Exploitation

An attacker can exploit this by sending an HTTP request with a spoofed X-Forwarded-For header to a CodeIgniter application deployed behind a reverse proxy. No authentication is required. The framework returns the spoofed IP as the client IP, effectively bypassing IP-based security measures [1][2].

Impact

Successful exploitation allows an attacker to impersonate any IP address, potentially bypassing IP-based access controls, logging, or rate limiting. This could lead to further attacks such as session hijacking or privilege escalation if the application relies on IP address for trust decisions [1].

Mitigation

The vulnerability is patched in CodeIgniter 4.2.11. Users should upgrade and configure Config\App::$proxyIPs as an array mapping trusted proxy IP addresses to the corresponding header names. As a workaround, avoid using $request->getIPAddress() [1][2][3].

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
codeigniter4/frameworkPackagist
< 4.2.114.2.11

Affected products

3

Patches

1
5ca8c99b2db0

Merge pull request from GHSA-ghw3-5qvm-3mqc

11 files changed · +314 112
  • app/Config/App.php+10 7 modified
    @@ -332,18 +332,21 @@ class App extends BaseConfig
          *
          * If your server is behind a reverse proxy, you must whitelist the proxy
          * IP addresses from which CodeIgniter should trust headers such as
    -     * HTTP_X_FORWARDED_FOR and HTTP_CLIENT_IP in order to properly identify
    +     * X-Forwarded-For or Client-IP in order to properly identify
          * the visitor's IP address.
          *
    -     * You can use both an array or a comma-separated list of proxy addresses,
    -     * as well as specifying whole subnets. Here are a few examples:
    +     * You need to set a proxy IP address or IP address with subnets and
    +     * the HTTP header for the client IP address.
          *
    -     * Comma-separated: '10.0.1.200,192.168.5.0/24'
    -     * Array: ['10.0.1.200', '192.168.5.0/24']
    +     * Here are some examples:
    +     *     [
    +     *         '10.0.1.200'     => 'X-Forwarded-For',
    +     *         '192.168.5.0/24' => 'X-Real-IP',
    +     *     ]
          *
    -     * @var string|string[]
    +     * @var array<string, string>
          */
    -    public $proxyIPs = '';
    +    public $proxyIPs = [];
     
         /**
          * --------------------------------------------------------------------------
    
  • phpstan-baseline.neon.dist+0 5 modified
    @@ -500,11 +500,6 @@ parameters:
     			count: 1
     			path: system/HTTP/RedirectResponse.php
     
    -		-
    -			message: "#^Property CodeIgniter\\\\HTTP\\\\Request\\:\\:\\$proxyIPs \\(array\\|string\\) on left side of \\?\\? is not nullable\\.$#"
    -			count: 1
    -			path: system/HTTP/Request.php
    -
     		-
     			message: "#^Property CodeIgniter\\\\HTTP\\\\Request\\:\\:\\$uri \\(CodeIgniter\\\\HTTP\\\\URI\\) in empty\\(\\) is not falsy\\.$#"
     			count: 1
    
  • system/HTTP/Request.php+1 1 modified
    @@ -23,7 +23,7 @@ class Request extends Message implements MessageInterface, RequestInterface
         /**
          * Proxy IPs
          *
    -     * @var array|string
    +     * @var array<string, string>
          *
          * @deprecated Check the App config directly
          */
    
  • system/HTTP/RequestTrait.php+89 65 modified
    @@ -11,6 +11,7 @@
     
     namespace CodeIgniter\HTTP;
     
    +use CodeIgniter\Exceptions\ConfigException;
     use CodeIgniter\Validation\FormatRules;
     
     /**
    @@ -43,7 +44,9 @@ trait RequestTrait
         /**
          * Gets the user's IP address.
          *
    -     * @return string IP address
    +     * @return string IP address if it can be detected, or empty string.
    +     *                If the IP address is not a valid IP address,
    +     *                then will return '0.0.0.0'.
          */
         public function getIPAddress(): string
         {
    @@ -59,93 +62,86 @@ public function getIPAddress(): string
             /**
              * @deprecated $this->proxyIPs property will be removed in the future
              */
    +        // @phpstan-ignore-next-line
             $proxyIPs = $this->proxyIPs ?? config('App')->proxyIPs;
    -        if (! empty($proxyIPs) && ! is_array($proxyIPs)) {
    -            $proxyIPs = explode(',', str_replace(' ', '', $proxyIPs));
    +        if (! empty($proxyIPs)) {
    +            // @phpstan-ignore-next-line
    +            if (! is_array($proxyIPs) || is_int(array_key_first($proxyIPs))) {
    +                throw new ConfigException(
    +                    'You must set an array with Proxy IP address key and HTTP header name value in Config\App::$proxyIPs.'
    +                );
    +            }
             }
     
             $this->ipAddress = $this->getServer('REMOTE_ADDR');
     
             if ($proxyIPs) {
    -            foreach (['x-forwarded-for', 'client-ip', 'x-client-ip', 'x-cluster-client-ip'] as $header) {
    -                $spoof     = null;
    -                $headerObj = $this->header($header);
    -
    -                if ($headerObj !== null) {
    -                    $spoof = $headerObj->getValue();
    -
    -                    // Some proxies typically list the whole chain of IP
    -                    // addresses through which the client has reached us.
    -                    // e.g. client_ip, proxy_ip1, proxy_ip2, etc.
    -                    sscanf($spoof, '%[^,]', $spoof);
    -
    -                    if (! $ipValidator($spoof)) {
    -                        $spoof = null;
    -                    } else {
    -                        break;
    -                    }
    -                }
    -            }
    -
    -            if ($spoof) {
    -                foreach ($proxyIPs as $proxyIP) {
    -                    // Check if we have an IP address or a subnet
    -                    if (strpos($proxyIP, '/') === false) {
    -                        // An IP address (and not a subnet) is specified.
    -                        // We can compare right away.
    -                        if ($proxyIP === $this->ipAddress) {
    +            // @TODO Extract all this IP address logic to another class.
    +            foreach ($proxyIPs as $proxyIP => $header) {
    +                // Check if we have an IP address or a subnet
    +                if (strpos($proxyIP, '/') === false) {
    +                    // An IP address (and not a subnet) is specified.
    +                    // We can compare right away.
    +                    if ($proxyIP === $this->ipAddress) {
    +                        $spoof = $this->getClientIP($header);
    +
    +                        if ($spoof !== null) {
                                 $this->ipAddress = $spoof;
                                 break;
                             }
    -
    -                        continue;
                         }
     
    -                    // We have a subnet ... now the heavy lifting begins
    -                    if (! isset($separator)) {
    -                        $separator = $ipValidator($this->ipAddress, 'ipv6') ? ':' : '.';
    -                    }
    +                    continue;
    +                }
     
    -                    // If the proxy entry doesn't match the IP protocol - skip it
    -                    if (strpos($proxyIP, $separator) === false) {
    -                        continue;
    -                    }
    +                // We have a subnet ... now the heavy lifting begins
    +                if (! isset($separator)) {
    +                    $separator = $ipValidator($this->ipAddress, 'ipv6') ? ':' : '.';
    +                }
     
    -                    // Convert the REMOTE_ADDR IP address to binary, if needed
    -                    if (! isset($ip, $sprintf)) {
    -                        if ($separator === ':') {
    -                            // Make sure we're have the "full" IPv6 format
    -                            $ip = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($this->ipAddress, ':')), $this->ipAddress));
    +                // If the proxy entry doesn't match the IP protocol - skip it
    +                if (strpos($proxyIP, $separator) === false) {
    +                    continue;
    +                }
     
    -                            for ($j = 0; $j < 8; $j++) {
    -                                $ip[$j] = intval($ip[$j], 16);
    -                            }
    +                // Convert the REMOTE_ADDR IP address to binary, if needed
    +                if (! isset($ip, $sprintf)) {
    +                    if ($separator === ':') {
    +                        // Make sure we're having the "full" IPv6 format
    +                        $ip = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($this->ipAddress, ':')), $this->ipAddress));
     
    -                            $sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b';
    -                        } else {
    -                            $ip      = explode('.', $this->ipAddress);
    -                            $sprintf = '%08b%08b%08b%08b';
    +                        for ($j = 0; $j < 8; $j++) {
    +                            $ip[$j] = intval($ip[$j], 16);
                             }
     
    -                        $ip = vsprintf($sprintf, $ip);
    +                        $sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b';
    +                    } else {
    +                        $ip      = explode('.', $this->ipAddress);
    +                        $sprintf = '%08b%08b%08b%08b';
                         }
     
    -                    // Split the netmask length off the network address
    -                    sscanf($proxyIP, '%[^/]/%d', $netaddr, $masklen);
    +                    $ip = vsprintf($sprintf, $ip);
    +                }
     
    -                    // Again, an IPv6 address is most likely in a compressed form
    -                    if ($separator === ':') {
    -                        $netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr));
    +                // Split the netmask length off the network address
    +                sscanf($proxyIP, '%[^/]/%d', $netaddr, $masklen);
     
    -                        for ($i = 0; $i < 8; $i++) {
    -                            $netaddr[$i] = intval($netaddr[$i], 16);
    -                        }
    -                    } else {
    -                        $netaddr = explode('.', $netaddr);
    +                // Again, an IPv6 address is most likely in a compressed form
    +                if ($separator === ':') {
    +                    $netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr));
    +
    +                    for ($i = 0; $i < 8; $i++) {
    +                        $netaddr[$i] = intval($netaddr[$i], 16);
                         }
    +                } else {
    +                    $netaddr = explode('.', $netaddr);
    +                }
    +
    +                // Convert to binary and finally compare
    +                if (strncmp($ip, vsprintf($sprintf, $netaddr), $masklen) === 0) {
    +                    $spoof = $this->getClientIP($header);
     
    -                    // Convert to binary and finally compare
    -                    if (strncmp($ip, vsprintf($sprintf, $netaddr), $masklen) === 0) {
    +                    if ($spoof !== null) {
                             $this->ipAddress = $spoof;
                             break;
                         }
    @@ -160,6 +156,34 @@ public function getIPAddress(): string
             return empty($this->ipAddress) ? '' : $this->ipAddress;
         }
     
    +    /**
    +     * Gets the client IP address from the HTTP header.
    +     */
    +    private function getClientIP(string $header): ?string
    +    {
    +        $ipValidator = [
    +            new FormatRules(),
    +            'valid_ip',
    +        ];
    +        $spoof     = null;
    +        $headerObj = $this->header($header);
    +
    +        if ($headerObj !== null) {
    +            $spoof = $headerObj->getValue();
    +
    +            // Some proxies typically list the whole chain of IP
    +            // addresses through which the client has reached us.
    +            // e.g. client_ip, proxy_ip1, proxy_ip2, etc.
    +            sscanf($spoof, '%[^,]', $spoof);
    +
    +            if (! $ipValidator($spoof)) {
    +                $spoof = null;
    +            }
    +        }
    +
    +        return $spoof;
    +    }
    +
         /**
          * Fetch an item from the $_SERVER array.
          *
    
  • system/Test/Mock/MockAppConfig.php+1 1 modified
    @@ -23,7 +23,7 @@ class MockAppConfig extends App
         public $cookieSecure     = false;
         public $cookieHTTPOnly   = false;
         public $cookieSameSite   = 'Lax';
    -    public $proxyIPs         = '';
    +    public $proxyIPs         = [];
         public $CSRFTokenName    = 'csrf_test_name';
         public $CSRFHeaderName   = 'X-CSRF-TOKEN';
         public $CSRFCookieName   = 'csrf_cookie_name';
    
  • system/Test/Mock/MockCLIConfig.php+1 1 modified
    @@ -23,7 +23,7 @@ class MockCLIConfig extends App
         public $cookieSecure     = false;
         public $cookieHTTPOnly   = false;
         public $cookieSameSite   = 'Lax';
    -    public $proxyIPs         = '';
    +    public $proxyIPs         = [];
         public $CSRFTokenName    = 'csrf_test_name';
         public $CSRFCookieName   = 'csrf_cookie_name';
         public $CSRFExpire       = 7200;
    
  • tests/system/HTTP/IncomingRequestTest.php+154 14 modified
    @@ -11,6 +11,7 @@
     
     namespace CodeIgniter\HTTP;
     
    +use CodeIgniter\Exceptions\ConfigException;
     use CodeIgniter\HTTP\Exceptions\HTTPException;
     use CodeIgniter\HTTP\Files\UploadedFile;
     use CodeIgniter\Test\CIUnitTestCase;
    @@ -714,24 +715,64 @@ public function testGetIPAddressThruProxy()
             $expected                        = '123.123.123.123';
             $_SERVER['HTTP_X_FORWARDED_FOR'] = $expected;
             $_SERVER['REMOTE_ADDR']          = '10.0.1.200';
    -        $config                          = new App();
    -        $config->proxyIPs                = '10.0.1.200,192.168.5.0/24';
     
    +        $config           = new App();
    +        $config->proxyIPs = [
    +            '10.0.1.200'     => 'X-Forwarded-For',
    +            '192.168.5.0/24' => 'X-Forwarded-For',
    +        ];
             $this->request = new Request($config);
             $this->request->populateHeaders();
     
             // we should see the original forwarded address
             $this->assertSame($expected, $this->request->getIPAddress());
         }
     
    -    public function testGetIPAddressThruProxyInvalid()
    +    public function testGetIPAddressThruProxyIPv6()
    +    {
    +        $expected                        = '123.123.123.123';
    +        $_SERVER['HTTP_X_FORWARDED_FOR'] = $expected;
    +        $_SERVER['REMOTE_ADDR']          = '2001:db8::2:1';
    +
    +        $config           = new App();
    +        $config->proxyIPs = [
    +            '2001:db8::2:1' => 'X-Forwarded-For',
    +        ];
    +        $this->request = new Request($config);
    +        $this->request->populateHeaders();
    +
    +        // we should see the original forwarded address
    +        $this->assertSame($expected, $this->request->getIPAddress());
    +    }
    +
    +    public function testGetIPAddressThruProxyInvalidIPAddress()
         {
             $_SERVER['HTTP_X_FORWARDED_FOR'] = '123.456.23.123';
             $expected                        = '10.0.1.200';
             $_SERVER['REMOTE_ADDR']          = $expected;
    -        $config                          = new App();
    -        $config->proxyIPs                = '10.0.1.200,192.168.5.0/24';
     
    +        $config           = new App();
    +        $config->proxyIPs = [
    +            '10.0.1.200'     => 'X-Forwarded-For',
    +            '192.168.5.0/24' => 'X-Forwarded-For',
    +        ];
    +        $this->request = new Request($config);
    +        $this->request->populateHeaders();
    +
    +        // spoofed address invalid
    +        $this->assertSame($expected, $this->request->getIPAddress());
    +    }
    +
    +    public function testGetIPAddressThruProxyInvalidIPAddressIPv6()
    +    {
    +        $_SERVER['HTTP_X_FORWARDED_FOR'] = '2001:xyz::1';
    +        $expected                        = '2001:db8::2:1';
    +        $_SERVER['REMOTE_ADDR']          = $expected;
    +
    +        $config           = new App();
    +        $config->proxyIPs = [
    +            '2001:db8::2:1' => 'X-Forwarded-For',
    +        ];
             $this->request = new Request($config);
             $this->request->populateHeaders();
     
    @@ -744,9 +785,29 @@ public function testGetIPAddressThruProxyNotWhitelisted()
             $expected                        = '10.10.1.200';
             $_SERVER['REMOTE_ADDR']          = $expected;
             $_SERVER['HTTP_X_FORWARDED_FOR'] = '123.456.23.123';
    -        $config                          = new App();
    -        $config->proxyIPs                = '10.0.1.200,192.168.5.0/24';
     
    +        $config           = new App();
    +        $config->proxyIPs = [
    +            '10.0.1.200'     => 'X-Forwarded-For',
    +            '192.168.5.0/24' => 'X-Forwarded-For',
    +        ];
    +        $this->request = new Request($config);
    +        $this->request->populateHeaders();
    +
    +        // spoofed address invalid
    +        $this->assertSame($expected, $this->request->getIPAddress());
    +    }
    +
    +    public function testGetIPAddressThruProxyNotWhitelistedIPv6()
    +    {
    +        $expected                        = '2001:db8::2:2';
    +        $_SERVER['REMOTE_ADDR']          = $expected;
    +        $_SERVER['HTTP_X_FORWARDED_FOR'] = '123.456.23.123';
    +
    +        $config           = new App();
    +        $config->proxyIPs = [
    +            '2001:db8::2:1' => 'X-Forwarded-For',
    +        ];
             $this->request = new Request($config);
             $this->request->populateHeaders();
     
    @@ -759,29 +820,108 @@ public function testGetIPAddressThruProxySubnet()
             $expected                        = '123.123.123.123';
             $_SERVER['HTTP_X_FORWARDED_FOR'] = $expected;
             $_SERVER['REMOTE_ADDR']          = '192.168.5.21';
    -        $config                          = new App();
    -        $config->proxyIPs                = ['192.168.5.0/24'];
     
    -        $this->request = new Request($config);
    +        $config           = new App();
    +        $config->proxyIPs = ['192.168.5.0/24' => 'X-Forwarded-For'];
    +        $this->request    = new Request($config);
    +        $this->request->populateHeaders();
    +
    +        // we should see the original forwarded address
    +        $this->assertSame($expected, $this->request->getIPAddress());
    +    }
    +
    +    public function testGetIPAddressThruProxySubnetIPv6()
    +    {
    +        $expected                        = '123.123.123.123';
    +        $_SERVER['HTTP_X_FORWARDED_FOR'] = $expected;
    +        $_SERVER['REMOTE_ADDR']          = '2001:db8:1234:ffff:ffff:ffff:ffff:ffff';
    +
    +        $config           = new App();
    +        $config->proxyIPs = ['2001:db8:1234::/48' => 'X-Forwarded-For'];
    +        $this->request    = new Request($config);
             $this->request->populateHeaders();
     
             // we should see the original forwarded address
             $this->assertSame($expected, $this->request->getIPAddress());
         }
     
    -    public function testGetIPAddressThruProxyOutofSubnet()
    +    public function testGetIPAddressThruProxyOutOfSubnet()
         {
             $expected                        = '192.168.5.21';
             $_SERVER['REMOTE_ADDR']          = $expected;
    -        $config                          = new App();
    -        $config->proxyIPs                = ['192.168.5.0/28'];
             $_SERVER['HTTP_X_FORWARDED_FOR'] = '123.123.123.123';
    -        $this->request                   = new Request($config);
    +
    +        $config           = new App();
    +        $config->proxyIPs = ['192.168.5.0/28' => 'X-Forwarded-For'];
    +        $this->request    = new Request($config);
    +        $this->request->populateHeaders();
    +
    +        // we should see the original forwarded address
    +        $this->assertSame($expected, $this->request->getIPAddress());
    +    }
    +
    +    public function testGetIPAddressThruProxyOutOfSubnetIPv6()
    +    {
    +        $expected                        = '2001:db8:1235:ffff:ffff:ffff:ffff:ffff';
    +        $_SERVER['REMOTE_ADDR']          = $expected;
    +        $_SERVER['HTTP_X_FORWARDED_FOR'] = '123.123.123.123';
    +
    +        $config           = new App();
    +        $config->proxyIPs = ['2001:db8:1234::/48' => 'X-Forwarded-For'];
    +        $this->request    = new Request($config);
    +        $this->request->populateHeaders();
    +
    +        // we should see the original forwarded address
    +        $this->assertSame($expected, $this->request->getIPAddress());
    +    }
    +
    +    public function testGetIPAddressThruProxyBothIPv4AndIPv6()
    +    {
    +        $expected                        = '2001:db8:1235:ffff:ffff:ffff:ffff:ffff';
    +        $_SERVER['REMOTE_ADDR']          = $expected;
    +        $_SERVER['HTTP_X_FORWARDED_FOR'] = '123.123.123.123';
    +
    +        $config           = new App();
    +        $config->proxyIPs = [
    +            '192.168.5.0/28'     => 'X-Forwarded-For',
    +            '2001:db8:1234::/48' => 'X-Forwarded-For',
    +        ];
    +        $this->request = new Request($config);
             $this->request->populateHeaders();
     
             // we should see the original forwarded address
             $this->assertSame($expected, $this->request->getIPAddress());
         }
     
    +    public function testGetIPAddressThruProxyInvalidConfigString()
    +    {
    +        $this->expectException(ConfigException::class);
    +        $this->expectExceptionMessage(
    +            'You must set an array with Proxy IP address key and HTTP header name value in Config\App::$proxyIPs.'
    +        );
    +
    +        $config           = new App();
    +        $config->proxyIPs = '192.168.5.0/28';
    +        $this->request    = new Request($config);
    +        $this->request->populateHeaders();
    +
    +        $this->request->getIPAddress();
    +    }
    +
    +    public function testGetIPAddressThruProxyInvalidConfigArray()
    +    {
    +        $this->expectException(ConfigException::class);
    +        $this->expectExceptionMessage(
    +            'You must set an array with Proxy IP address key and HTTP header name value in Config\App::$proxyIPs.'
    +        );
    +
    +        $config           = new App();
    +        $config->proxyIPs = ['192.168.5.0/28'];
    +        $this->request    = new Request($config);
    +        $this->request->populateHeaders();
    +
    +        $this->request->getIPAddress();
    +    }
    +
         // @TODO getIPAddress should have more testing, to 100% code coverage
     }
    
  • tests/system/HTTP/RequestTest.php+29 15 modified
    @@ -609,10 +609,14 @@ public function testGetIPAddressThruProxy()
         {
             $expected                        = '123.123.123.123';
             $_SERVER['REMOTE_ADDR']          = '10.0.1.200';
    -        $config                          = new App();
    -        $config->proxyIPs                = '10.0.1.200,192.168.5.0/24';
             $_SERVER['HTTP_X_FORWARDED_FOR'] = $expected;
    -        $this->request                   = new Request($config);
    +
    +        $config           = new App();
    +        $config->proxyIPs = [
    +            '10.0.1.200'     => 'X-Forwarded-For',
    +            '192.168.5.0/24' => 'X-Forwarded-For',
    +        ];
    +        $this->request = new Request($config);
             $this->request->populateHeaders();
     
             // we should see the original forwarded address
    @@ -623,10 +627,14 @@ public function testGetIPAddressThruProxyInvalid()
         {
             $expected                        = '123.456.23.123';
             $_SERVER['REMOTE_ADDR']          = '10.0.1.200';
    -        $config                          = new App();
    -        $config->proxyIPs                = '10.0.1.200,192.168.5.0/24';
             $_SERVER['HTTP_X_FORWARDED_FOR'] = $expected;
    -        $this->request                   = new Request($config);
    +        $config                          = new App();
    +        $config->proxyIPs                = [
    +            '10.0.1.200'     => 'X-Forwarded-For',
    +            '192.168.5.0/24' => 'X-Forwarded-For',
    +        ];
    +
    +        $this->request = new Request($config);
             $this->request->populateHeaders();
     
             // spoofed address invalid
    @@ -637,10 +645,14 @@ public function testGetIPAddressThruProxyNotWhitelisted()
         {
             $expected                        = '123.456.23.123';
             $_SERVER['REMOTE_ADDR']          = '10.10.1.200';
    -        $config                          = new App();
    -        $config->proxyIPs                = '10.0.1.200,192.168.5.0/24';
             $_SERVER['HTTP_X_FORWARDED_FOR'] = $expected;
    -        $this->request                   = new Request($config);
    +
    +        $config           = new App();
    +        $config->proxyIPs = [
    +            '10.0.1.200'     => 'X-Forwarded-For',
    +            '192.168.5.0/24' => 'X-Forwarded-For',
    +        ];
    +        $this->request = new Request($config);
             $this->request->populateHeaders();
     
             // spoofed address invalid
    @@ -651,10 +663,11 @@ public function testGetIPAddressThruProxySubnet()
         {
             $expected                        = '123.123.123.123';
             $_SERVER['REMOTE_ADDR']          = '192.168.5.21';
    -        $config                          = new App();
    -        $config->proxyIPs                = ['192.168.5.0/24'];
             $_SERVER['HTTP_X_FORWARDED_FOR'] = $expected;
    -        $this->request                   = new Request($config);
    +
    +        $config           = new App();
    +        $config->proxyIPs = ['192.168.5.0/24' => 'X-Forwarded-For'];
    +        $this->request    = new Request($config);
             $this->request->populateHeaders();
     
             // we should see the original forwarded address
    @@ -665,10 +678,11 @@ public function testGetIPAddressThruProxyOutofSubnet()
         {
             $expected                        = '123.123.123.123';
             $_SERVER['REMOTE_ADDR']          = '192.168.5.21';
    -        $config                          = new App();
    -        $config->proxyIPs                = ['192.168.5.0/28'];
             $_SERVER['HTTP_X_FORWARDED_FOR'] = $expected;
    -        $this->request                   = new Request($config);
    +
    +        $config           = new App();
    +        $config->proxyIPs = ['192.168.5.0/28' => 'X-Forwarded-For'];
    +        $this->request    = new Request($config);
             $this->request->populateHeaders();
     
             // we should see the original forwarded address
    
  • user_guide_src/source/changelogs/v4.2.11.rst+10 0 modified
    @@ -9,6 +9,16 @@ Release Date: December 21, 2022
         :local:
         :depth: 2
     
    +SECURITY
    +********
    +
    +- *Attackers may spoof IP address when using proxy* was fixed. See the `Security advisory GHSA-ghw3-5qvm-3mqc <https://github.com/codeigniter4/CodeIgniter4/security/advisories/GHSA-ghw3-5qvm-3mqc>`_ for more information.
    +
    +BREAKING
    +********
    +
    +- The ``Config\App::$proxyIPs`` value format has been changed. See :ref:`Upgrading Guide <upgrade-4211-proxyips>`.
    +
     Bugs Fixed
     **********
     
    
  • user_guide_src/source/incoming/request.rst+2 3 modified
    @@ -28,9 +28,8 @@ Class Reference
     
             .. literalinclude:: request/001.php
     
    -        .. important:: This method takes into account the ``App->proxyIPs`` setting and will
    -            return the reported HTTP_X_FORWARDED_FOR, HTTP_CLIENT_IP, HTTP_X_CLIENT_IP, or
    -            HTTP_X_CLUSTER_CLIENT_IP address for the allowed IP address.
    +        .. important:: This method takes into account the ``Config\App::$proxyIPs`` setting and will
    +            return the reported client IP address by the HTTP header for the allowed IP address.
     
         .. php:method:: isValidIP($ip[, $which = ''])
     
    
  • user_guide_src/source/installation/upgrade_4211.rst+17 0 modified
    @@ -12,6 +12,23 @@ Please refer to the upgrade instructions corresponding to your installation meth
         :local:
         :depth: 2
     
    +Breaking Changes
    +****************
    +
    +.. _upgrade-4211-proxyips:
    +
    +Config\\App::$proxyIPs
    +======================
    +
    +The config value format has been changed. Now you must set your proxy IP address and the HTTP header name for the client IP address pair as an array::
    +
    +    public $proxyIPs = [
    +            '10.0.1.200'     => 'X-Forwarded-For',
    +            '192.168.5.0/24' => 'X-Forwarded-For',
    +    ];
    +
    +``ConfigException`` will be thrown for old format config value.
    +
     Project Files
     *************
     
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.