API Fix HTTPS proxy header detection

Didn't use the de facto standard HTTP_X_FORWARDED_PROTO or the less standard HTTP_FRONT_END_HTTPS.
Removed the 'X-Forwarded-Proto', since PHP should prefix/underscore all HTTP headers before it hits $_SERVER.

References:
- https://docs.djangoproject.com/en/1.4/ref/settings/#secure-proxy-ssl-header
- https://drupal.org/node/1859252
- https://drupal.org/node/313145
- http://scottwb.com/blog/2013/02/06/always-on-https-with-rails-behind-an-elb/
This commit is contained in:
Ingo Schommer 2014-05-22 18:34:15 +12:00
parent 6466e390af
commit ec325a3c7f
4 changed files with 67 additions and 24 deletions

View File

@ -478,29 +478,37 @@ class Director implements TemplateGlobalProvider {
* @return boolean * @return boolean
*/ */
public static function is_https() { public static function is_https() {
$return = false;
if ($protocol = Config::inst()->get('Director', 'alternate_protocol')) { if ($protocol = Config::inst()->get('Director', 'alternate_protocol')) {
return $protocol == 'https'; $return = ($protocol == 'https');
} } else if(
isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
if(isset($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])) { && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https'
if(strtolower($_SERVER['HTTP_X_FORWARDED_PROTOCOL']) == 'https') { ) {
return true; // Convention for (non-standard) proxy signaling a HTTPS forward,
} // see https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
} $return = true;
} else if(
if(isset($_SERVER['X-Forwarded-Proto'])) { isset($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])
if(strtolower($_SERVER['X-Forwarded-Proto']) == "https") { && strtolower($_SERVER['HTTP_X_FORWARDED_PROTOCOL']) == 'https'
return true; ) {
} // Less conventional proxy header
} $return = true;
} else if(
if((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')) { isset($_SERVER['HTTP_FRONT_END_HTTPS'])
return true; && strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) == 'on'
) {
// Microsoft proxy convention: https://support.microsoft.com/?kbID=307347
$return = true;
} else if((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')) {
$return = true;
} else if(isset($_SERVER['SSL'])) { } else if(isset($_SERVER['SSL'])) {
return true; $return = true;
} else {
$return = false;
} }
return false; return $return;
} }
/** /**

View File

@ -377,7 +377,7 @@ class HTTP {
// By also using and etag that includes both the modification date and all the varies // By also using and etag that includes both the modification date and all the varies
// values which we also check against we can catch this and not return a 304 // values which we also check against we can catch this and not return a 304
$etagParts = array(self::$modification_date, serialize($_COOKIE)); $etagParts = array(self::$modification_date, serialize($_COOKIE));
if (isset($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])) $etagParts[] = $_SERVER['HTTP_X_FORWARDED_PROTOCOL']; $etagParts[] = Director::is_https() ? 'https' : 'http';
if (isset($_SERVER['HTTP_USER_AGENT'])) $etagParts[] = $_SERVER['HTTP_USER_AGENT']; if (isset($_SERVER['HTTP_USER_AGENT'])) $etagParts[] = $_SERVER['HTTP_USER_AGENT'];
if (isset($_SERVER['HTTP_ACCEPT'])) $etagParts[] = $_SERVER['HTTP_ACCEPT']; if (isset($_SERVER['HTTP_ACCEPT'])) $etagParts[] = $_SERVER['HTTP_ACCEPT'];

View File

@ -76,11 +76,31 @@ class ParameterConfirmationToken {
protected function currentAbsoluteURL() { protected function currentAbsoluteURL() {
global $url; global $url;
// Are we http or https? // Are we http or https? Replicates Director::is_https() without its dependencies/
$proto = 'http'; $proto = 'http';
if(
if(isset($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])) { isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
if(strtolower($_SERVER['HTTP_X_FORWARDED_PROTOCOL']) == 'https') $proto = 'https'; && 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(
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
$proto = 'https';
} else if((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')) {
$proto = 'https';
} else if(isset($_SERVER['SSL'])) {
$proto = 'https';
} }
if((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')) $proto = 'https'; if((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')) $proto = 'https';

View File

@ -370,6 +370,21 @@ class DirectorTest extends SapphireTest {
$_SERVER['HTTP_X_FORWARDED_PROTOCOL'] = 'ftp'; $_SERVER['HTTP_X_FORWARDED_PROTOCOL'] = 'ftp';
$this->assertFalse(Director::is_https()); $this->assertFalse(Director::is_https());
$_SERVER['HTTP_X_FORWARDED_PROTO'] = 'https';
$this->assertTrue(Director::is_https());
$_SERVER['HTTP_X_FORWARDED_PROTO'] = 'http';
$this->assertFalse(Director::is_https());
$_SERVER['HTTP_X_FORWARDED_PROTO'] = 'ftp';
$this->assertFalse(Director::is_https());
$_SERVER['HTTP_FRONT_END_HTTPS'] = 'On';
$this->assertTrue(Director::is_https());
$_SERVER['HTTP_FRONT_END_HTTPS'] = 'Off';
$this->assertFalse(Director::is_https());
// https via HTTPS // https via HTTPS
$_SERVER['HTTPS'] = 'true'; $_SERVER['HTTPS'] = 'true';
$this->assertTrue(Director::is_https()); $this->assertTrue(Director::is_https());