[ss-2016-003] Hostname, IP and Protocol Spoofing through HTTP Headers

This commit is contained in:
Ingo Schommer 2016-02-17 23:19:19 +13:00 committed by Damian Mooyman
parent e2c77c5a8f
commit faa94d51d5
5 changed files with 68 additions and 53 deletions

View File

@ -507,28 +507,28 @@ class Director implements TemplateGlobalProvider {
*/
public static function is_https() {
$return = false;
// See https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
// See https://support.microsoft.com/?kbID=307347
$headerOverride = false;
if(TRUSTED_PROXY) {
$headers = (defined('SS_TRUSTED_PROXY_PROTOCOL_HEADER')) ? array(SS_TRUSTED_PROXY_PROTOCOL_HEADER) : null;
if(!$headers) {
// Backwards compatible defaults
$headers = array('HTTP_X_FORWARDED_PROTO', 'HTTP_X_FORWARDED_PROTOCOL', 'HTTP_FRONT_END_HTTPS');
}
foreach($headers as $header) {
$headerCompareVal = ($header === 'HTTP_FRONT_END_HTTPS' ? 'on' : 'https');
if(!empty($_SERVER[$header]) && strtolower($_SERVER[$header]) == $headerCompareVal) {
$headerOverride = true;
break;
}
}
}
if ($protocol = Config::inst()->get('Director', 'alternate_protocol')) {
$return = ($protocol == 'https');
} else if(
TRUSTED_PROXY
&& isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
&& strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https'
) {
// Convention for (non-standard) proxy signaling a HTTPS forward,
// see https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
$return = true;
} else if(
TRUSTED_PROXY
&& isset($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])
&& strtolower($_SERVER['HTTP_X_FORWARDED_PROTOCOL']) == 'https'
) {
// Less conventional proxy header
$return = true;
} else if(
isset($_SERVER['HTTP_FRONT_END_HTTPS'])
&& strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) == 'on'
) {
// Microsoft proxy convention: https://support.microsoft.com/?kbID=307347
} else if($headerOverride) {
$return = true;
} else if((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')) {
$return = true;

View File

@ -655,14 +655,27 @@ class SS_HTTPRequest implements ArrayAccess {
* @return string
*/
public function getIP() {
if (TRUSTED_PROXY && !empty($_SERVER['HTTP_CLIENT_IP'])) {
//check ip from share internet
return $_SERVER['HTTP_CLIENT_IP'];
} elseif (TRUSTED_PROXY && !empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
//to check ip is pass from proxy
return $_SERVER['HTTP_X_FORWARDED_FOR'];
$headerOverrideIP = null;
if(TRUSTED_PROXY) {
$headers = (defined('SS_TRUSTED_PROXY_IP_HEADER')) ? array(SS_TRUSTED_PROXY_IP_HEADER) : null;
if(!$headers) {
// Backwards compatible defaults
$headers = array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR');
}
foreach($headers as $header) {
if(!empty($_SERVER[$header])) {
$headerOverrideIP = $_SERVER[$header];
break;
}
}
}
if ($headerOverrideIP) {
return $headerOverrideIP;
} elseif(isset($_SERVER['REMOTE_ADDR'])) {
return $_SERVER['REMOTE_ADDR'];
} else {
return null;
}
}

View File

@ -179,10 +179,13 @@ if(!isset($_SERVER['HTTP_HOST'])) {
/**
* Fix HTTP_HOST from reverse proxies
*/
if (TRUSTED_PROXY && isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
$trustedProxyHeader = (defined('SS_TRUSTED_PROXY_HOST_HEADER'))
? SS_TRUSTED_PROXY_HOST_HEADER
: 'HTTP_X_FORWARDED_HOST';
if (TRUSTED_PROXY && !empty($_SERVER[$trustedProxyHeader])) {
// Get the first host, in case there's multiple separated through commas
$_SERVER['HTTP_HOST'] = strtok($_SERVER['HTTP_X_FORWARDED_HOST'], ',');
$_SERVER['HTTP_HOST'] = strtok($_SERVER[SS_TRUSTED_PROXY_HOST_HEADER], ',');
}
}

View File

@ -163,26 +163,25 @@ class ParameterConfirmationToken {
// Are we http or https? Replicates Director::is_https() without its dependencies/
$proto = 'http';
if(
TRUSTED_PROXY
&& isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
&& strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https'
) {
// Convention for (non-standard) proxy signaling a HTTPS forward,
// see https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
$proto = 'https';
} else if(
TRUSTED_PROXY
&& isset($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])
&& strtolower($_SERVER['HTTP_X_FORWARDED_PROTOCOL']) == 'https'
) {
// Less conventional proxy header
$proto = 'https';
} else if(
isset($_SERVER['HTTP_FRONT_END_HTTPS'])
&& strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) == 'on'
) {
// Microsoft proxy convention: https://support.microsoft.com/?kbID=307347
// See https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
// See https://support.microsoft.com/?kbID=307347
$headerOverride = false;
if(TRUSTED_PROXY) {
$headers = (defined('SS_TRUSTED_PROXY_PROTOCOL_HEADER')) ? array(SS_TRUSTED_PROXY_PROTOCOL_HEADER) : null;
if(!$headers) {
// Backwards compatible defaults
$headers = array('HTTP_X_FORWARDED_PROTO', 'HTTP_X_FORWARDED_PROTOCOL', 'HTTP_FRONT_END_HTTPS');
}
foreach($headers as $header) {
$headerCompareVal = ($header === 'HTTP_FRONT_END_HTTPS' ? 'on' : 'https');
if(!empty($_SERVER[$header]) && strtolower($_SERVER[$header]) == $headerCompareVal) {
$headerOverride = true;
break;
}
}
}
if($headerOverride) {
$proto = 'https';
} else if((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')) {
$proto = 'https';
@ -190,9 +189,6 @@ class ParameterConfirmationToken {
$proto = 'https';
}
if((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')) $proto = 'https';
if(isset($_SERVER['SSL'])) $proto = 'https';
$parts = array_filter(array(
// What's our host
$_SERVER['HTTP_HOST'],

View File

@ -580,7 +580,11 @@ server IPs using the SS_TRUSTED_PROXY_IPS define in your _ss_environment.php.
:::php
define('SS_TRUSTED_PROXY_IPS', '127.0.0.1,192.168.0.1');
define('SS_TRUSTED_PROXY_HOST_HEADER', 'HTTP_X_FORWARDED_HOST');
define('SS_TRUSTED_PROXY_IP_HEADER', 'HTTP_X_FORWARDED_FOR');
define('SS_TRUSTED_PROXY_PROTOCOL_HEADER', 'HTTP_X_FORWARDED_PROTOCOL');
At the same time, you'll also need to define which headers you trust from these proxy IPs. Since there are multiple ways through which proxies can pass through HTTP information on the original hostname, IP and protocol, these values need to be adjusted for your specific proxy. The header names match their equivalent `$_SERVER` values.
If there is no proxy server, 'none' can be used to distrust all clients.
If only trusted servers will make requests then you can use '*' to trust all clients.
@ -603,7 +607,6 @@ In a future release this behaviour will be changed to be on by default, and this
variable will be no longer necessary, thus it will be necessary to always set
SS_TRUSTED_PROXY_IPS if using a proxy.
## Related
* [http://silverstripe.org/security-releases/](http://silverstripe.org/security-releases/)