Commit 72e81af1 authored by Dries's avatar Dries
Browse files

- Patch #324875 by pwolanin, Damien Tournoud, goba: improved conf_path() header injection checking.

parent 05420fde
...@@ -347,11 +347,6 @@ function conf_path($require_settings = TRUE, $reset = FALSE) { ...@@ -347,11 +347,6 @@ function conf_path($require_settings = TRUE, $reset = FALSE) {
} }
$uri = explode('/', $_SERVER['SCRIPT_NAME'] ? $_SERVER['SCRIPT_NAME'] : $_SERVER['SCRIPT_FILENAME']); $uri = explode('/', $_SERVER['SCRIPT_NAME'] ? $_SERVER['SCRIPT_NAME'] : $_SERVER['SCRIPT_FILENAME']);
if (strpos($_SERVER['HTTP_HOST'], '/') !== FALSE || strpos($_SERVER['HTTP_HOST'], '\\') !== FALSE) {
// A HTTP_HOST containing slashes may be an attack and is invalid.
header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request');
exit;
}
$server = explode('.', implode('.', array_reverse(explode(':', rtrim($_SERVER['HTTP_HOST'], '.'))))); $server = explode('.', implode('.', array_reverse(explode(':', rtrim($_SERVER['HTTP_HOST'], '.')))));
for ($i = count($uri) - 1; $i > 0; $i--) { for ($i = count($uri) - 1; $i > 0; $i--) {
for ($j = count($server); $j > 0; $j--) { for ($j = count($server); $j > 0; $j--) {
...@@ -385,6 +380,21 @@ function drupal_initialize_variables() { ...@@ -385,6 +380,21 @@ function drupal_initialize_variables() {
ini_set('html_errors', 0); ini_set('html_errors', 0);
} }
/**
* Validate that $_SERVER['HTTP_HOST'] is safe.
*
* As $_SERVER['HTTP_HOST'] is user input, ensure it only contains characters
* allowed in hostnames. See RFC 952 (and RFC 2181). $_SERVER['HTTP_HOST'] is
* lowercased.
*
* @return
* TRUE if only containing valid characters, or FALSE otherwise.
*/
function drupal_valid_http_host() {
$_SERVER['HTTP_HOST'] = strtolower($_SERVER['HTTP_HOST']);
return preg_match('/^\[?(?:[a-z0-9-:\]_]+\.?)+$/', $_SERVER['HTTP_HOST']);
}
/** /**
* Loads the configuration and sets the base URL, cookie domain, and * Loads the configuration and sets the base URL, cookie domain, and
* session name correctly. * session name correctly.
...@@ -396,6 +406,12 @@ function conf_init() { ...@@ -396,6 +406,12 @@ function conf_init() {
global $databases, $db_prefix, $cookie_domain, $conf, $installed_profile, $update_free_access; global $databases, $db_prefix, $cookie_domain, $conf, $installed_profile, $update_free_access;
$conf = array(); $conf = array();
if (!drupal_valid_http_host()) {
// HTTP_HOST is invalid, e.g. if containing slashes it may be an attack.
header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request');
exit;
}
if (file_exists(DRUPAL_ROOT . '/' . conf_path() . '/settings.php')) { if (file_exists(DRUPAL_ROOT . '/' . conf_path() . '/settings.php')) {
include_once DRUPAL_ROOT . '/' . conf_path() . '/settings.php'; include_once DRUPAL_ROOT . '/' . conf_path() . '/settings.php';
} }
...@@ -414,9 +430,7 @@ function conf_init() { ...@@ -414,9 +430,7 @@ function conf_init() {
// Create base URL // Create base URL
$base_root = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http'; $base_root = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http';
// As $_SERVER['HTTP_HOST'] is user input, ensure it only contains $base_url = $base_root .= '://' . $_SERVER['HTTP_HOST'];
// characters allowed in hostnames.
$base_url = $base_root .= '://' . preg_replace('/[^a-z0-9-:._]/i', '', $_SERVER['HTTP_HOST']);
// $_SERVER['SCRIPT_NAME'] can, in contrast to $_SERVER['PHP_SELF'], not // $_SERVER['SCRIPT_NAME'] can, in contrast to $_SERVER['PHP_SELF'], not
// be modified by a visitor. // be modified by a visitor.
......
...@@ -8,8 +8,8 @@ class BootstrapIPAddressTestCase extends DrupalWebTestCase { ...@@ -8,8 +8,8 @@ class BootstrapIPAddressTestCase extends DrupalWebTestCase {
*/ */
function getInfo() { function getInfo() {
return array( return array(
'name' => t('IP address test'), 'name' => t('IP address and HTTP_HOST test'),
'description' => t('Get the IP address from the current visitor from the server variables.'), 'description' => t('Get the IP address from the current visitor from the server variables, check hostname validation.'),
'group' => t('Bootstrap') 'group' => t('Bootstrap')
); );
} }
...@@ -42,9 +42,9 @@ class BootstrapIPAddressTestCase extends DrupalWebTestCase { ...@@ -42,9 +42,9 @@ class BootstrapIPAddressTestCase extends DrupalWebTestCase {
} }
/** /**
* testIPAddress * test IP Address and hostname
*/ */
function testIPAddress() { function testIPAddressHost() {
// Test the normal IP address. // Test the normal IP address.
$this->assertTrue( $this->assertTrue(
ip_address(true) == $this->remote_ip, ip_address(true) == $this->remote_ip,
...@@ -80,6 +80,16 @@ class BootstrapIPAddressTestCase extends DrupalWebTestCase { ...@@ -80,6 +80,16 @@ class BootstrapIPAddressTestCase extends DrupalWebTestCase {
ip_address(true) == $this->cluster_ip, ip_address(true) == $this->cluster_ip,
t('Cluster environment got cluster client IP') t('Cluster environment got cluster client IP')
); );
$_SERVER['HTTP_HOST'] = 'security/.drupal.org:80';
$this->assertFalse(drupal_valid_http_host(), t('HTTP_HOST with / is invalid'));
$_SERVER['HTTP_HOST'] = 'security\\.drupal.org:80';
$this->assertFalse(drupal_valid_http_host(), t('HTTP_HOST with \\ is invalid'));
$_SERVER['HTTP_HOST'] = 'security<.drupal.org:80';
$this->assertFalse(drupal_valid_http_host(), t('HTTP_HOST with &lt; is invalid'));
$_SERVER['HTTP_HOST'] = 'security..drupal.org:80';
$this->assertFalse(drupal_valid_http_host(), t('HTTP_HOST with .. is invalid'));
$_SERVER['HTTP_HOST'] = '[::1]:80'; // IPv6 loopback address
$this->assertTrue(drupal_valid_http_host(), t('HTTP_HOST containing IPv6 loopback is valid'));
} }
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment