Skip to content
This repository has been archived by the owner on Jan 31, 2020. It is now read-only.

Commit

Permalink
Merge branch 'security/session-ip-validator'
Browse files Browse the repository at this point in the history
Fixes issues with proxy server/ip detection.

Fixes zendframework/zendframework#3095

- However, a different approach is taken than that used in that pull request.
  • Loading branch information
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 24 deletions.
62 changes: 42 additions & 20 deletions src/Validator/RemoteAddr.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace Zend\Session\Validator;

use Zend\Http\PhpEnvironment\RemoteAddress;
use Zend\Session\Validator\ValidatorInterface as SessionValidator;

/**
Expand Down Expand Up @@ -38,6 +39,20 @@ class RemoteAddr implements SessionValidator
*/
protected static $useProxy = false;

/**
* List of trusted proxy IP addresses
*
* @var array
*/
protected static $trustedProxies = array();

/**
* HTTP header to introspect for proxies
*
* @var string
*/
protected static $proxyHeader = 'HTTP_X_FORWARDED_FOR';

/**
* Constructor
* get the current user IP and store it in the session as 'valid data'
Expand Down Expand Up @@ -85,33 +100,40 @@ public static function getUseProxy()
return static::$useProxy;
}

/**
* Set list of trusted proxy addresses
*
* @param array $trustedProxies
* @return void
*/
public static function setTrustedProxies(array $trustedProxies)
{
static::$trustedProxies = $trustedProxies;
}

/**
* Set the header to introspect for proxy IPs
*
* @param string $header
* @return void
*/
public static function setProxyHeader($header = 'X-Forwarded-For')
{
static::$proxyHeader = $header;
}

/**
* Returns client IP address.
*
* @return string IP address.
*/
protected function getIpAddress()
{
if (static::$useProxy) {
// proxy IP address
if (isset($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP']) {
$ips = explode(',', $_SERVER['HTTP_CLIENT_IP']);
return trim($ips[0]);
}

// proxy IP address
if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR']) {
$ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
return trim($ips[0]);
}
}

// direct IP address
if (isset($_SERVER['REMOTE_ADDR'])) {
return $_SERVER['REMOTE_ADDR'];
}

return '';
$remoteAddress = new RemoteAddress();
$remoteAddress->setUseProxy(static::$useProxy);
$remoteAddress->setTrustedProxies(static::$trustedProxies);
$remoteAddress->setProxyHeader(static::$proxyHeader);
return $remoteAddress->getIpAddress();
}

/**
Expand Down
48 changes: 44 additions & 4 deletions test/Validator/RemoteAddrTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,17 @@ protected function backup()
$_SERVER['HTTP_X_FORWARDED_FOR'],
$_SERVER['HTTP_CLIENT_IP']
);
RemoteAddr::setUseProxy(false);
RemoteAddr::setTrustedProxies(array());
RemoteAddr::setProxyHeader();
}

protected function restore()
{
$_SERVER = $this->backup;
RemoteAddr::setUseProxy(false);
RemoteAddr::setTrustedProxies(array());
RemoteAddr::setProxyHeader();
}

public function testGetData()
Expand Down Expand Up @@ -84,7 +90,6 @@ public function testHttpXForwardedFor()
$_SERVER['HTTP_X_FORWARDED_FOR'] = '1.1.2.3';
RemoteAddr::setUseProxy(true);
$validator = new RemoteAddr();
RemoteAddr::setUseProxy(false);
$this->assertEquals('1.1.2.3', $validator->getData());
$this->restore();
}
Expand All @@ -97,20 +102,55 @@ public function testHttpClientIp()
$_SERVER['HTTP_X_FORWARDED_FOR'] = '2.1.2.3';
RemoteAddr::setUseProxy(true);
$validator = new RemoteAddr();
RemoteAddr::setUseProxy(false);
$this->assertEquals('2.1.2.3', $validator->getData());
$this->restore();
}

public function testMultipleHttpXForwardedFor()
public function testUsesRightMostAddressWhenMultipleHttpXForwardedForAddressesPresent()
{
$this->backup();
$_SERVER['REMOTE_ADDR'] = '0.1.2.3';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '2.1.2.3, 1.1.2.3';
RemoteAddr::setUseProxy(true);
$validator = new RemoteAddr();
RemoteAddr::setUseProxy(false);
$this->assertEquals('1.1.2.3', $validator->getData());
$this->restore();
}

public function testShouldNotUseClientIpHeaderToTestProxyCapabilitiesByDefault()
{
$this->backup();
$_SERVER['REMOTE_ADDR'] = '0.1.2.3';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '2.1.2.3, 1.1.2.3';
$_SERVER['HTTP_CLIENT_IP'] = '0.1.2.4';
RemoteAddr::setUseProxy(true);
$validator = new RemoteAddr();
$this->assertEquals('1.1.2.3', $validator->getData());
$this->restore();
}

public function testWillOmitTrustedProxyIpsFromXForwardedForMatching()
{
$this->backup();
$_SERVER['REMOTE_ADDR'] = '0.1.2.3';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '2.1.2.3, 1.1.2.3';
RemoteAddr::setUseProxy(true);
RemoteAddr::setTrustedProxies(array('1.1.2.3'));
$validator = new RemoteAddr();
$this->assertEquals('2.1.2.3', $validator->getData());
$this->restore();
}

public function testCanSpecifyWhichHeaderToUseStatically()
{
$this->backup();
$_SERVER['REMOTE_ADDR'] = '0.1.2.3';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '2.1.2.3, 1.1.2.3';
$_SERVER['HTTP_CLIENT_IP'] = '0.1.2.4';
RemoteAddr::setUseProxy(true);
RemoteAddr::setProxyHeader('Client-Ip');
$validator = new RemoteAddr();
$this->assertEquals('0.1.2.4', $validator->getData());
$this->restore();
}
}

0 comments on commit b27a1fe

Please sign in to comment.