[SECURITY] Add trusted HTTP_HOST configuration 99/30299/2
authorHelmut Hummel <helmut.hummel@typo3.org>
Thu, 22 May 2014 07:33:26 +0000 (09:33 +0200)
committerOliver Hader <oliver.hader@typo3.org>
Thu, 22 May 2014 07:33:31 +0000 (09:33 +0200)
TYPO3 uses the values of HTTP_HOST in several
places without validating them. This could
lead to a situation where links are generated
using the host part from HTTP_HOST.

Since HTTP_HOST headers are user input and
can be spoofed by an attacker, it leads
into several potential and actual security issues.

To address this, a configuration option for
trusted hosts is added, which is evaluated every
time getIndpEnv('HTTP_HOST') is called.

The configuration option is

$GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern']

and can contain either a regular expression or the
value "SERVER_NAME"

To properly output the exception message in case
the trustedHostPattern does not match,
we need to adapt the exception handlers slightly
to not log information in this case and to actually
show the message even in production context to not
confuse admins on what is currently going wrong.

To not break all existing installations, the default
pattern is set to 'SERVER_NAME' which allows all
HTTP_HOST values matching the SERVER_NAME (and
optionally the SERVER_PORT if a port is specified
in the HTTP_HOST value).

This will secure all installation which use properly
configured name based virtual hosts, but leaves
installations where the web server is not bound
to a specific host name still in an insecure state.

Change-Id: I38e6a18a3e66e80abda2a4682bd1348198de1f8b
Fixes: #30377
Releases: 6.2, 6.1, 6.0, 4.7, 4.5
Security-Bulletin: TYPO3-CORE-SA-2014-001
Reviewed-on: https://review.typo3.org/30299
Reviewed-by: Oliver Hader
Tested-by: Oliver Hader
typo3/sysext/core/Classes/Error/AbstractExceptionHandler.php
typo3/sysext/core/Classes/Error/ProductionExceptionHandler.php
typo3/sysext/core/Classes/Messaging/AbstractStandaloneMessage.php
typo3/sysext/core/Classes/Utility/GeneralUtility.php
typo3/sysext/core/Configuration/DefaultConfiguration.php
typo3/sysext/core/Tests/Unit/Utility/Fixtures/GeneralUtilityFixture.php [new file with mode: 0644]
typo3/sysext/core/Tests/Unit/Utility/GeneralUtilityTest.php

index e8affde..134daa4 100644 (file)
@@ -59,6 +59,10 @@ abstract class AbstractExceptionHandler implements \TYPO3\CMS\Core\Error\Excepti
         * @see \TYPO3\CMS\Core\Utility\GeneralUtility::sysLog(), \TYPO3\CMS\Core\Utility\GeneralUtility::devLog()
         */
        protected function writeLogEntries(\Exception $exception, $context) {
+               // Do not write any logs for this message to avoid filling up tables or files with illegal requests
+               if ($exception->getCode() === 1396795884) {
+                       return;
+               }
                $filePathAndName = $exception->getFile();
                $exceptionCodeNumber = $exception->getCode() > 0 ? '#' . $exception->getCode() . ': ' : '';
                $logTitle = 'Core: Exception handler (' . $context . ')';
index ee543f3..5375672 100644 (file)
@@ -23,6 +23,8 @@ namespace TYPO3\CMS\Core\Error;
  *
  *  This copyright notice MUST APPEAR in all copies of the script!
  ***************************************************************/
+use TYPO3\CMS\Core\Messaging\ErrorpageMessage;
+
 /**
  * A quite exception handler which catches but ignores any exception.
  *
@@ -86,6 +88,13 @@ class ProductionExceptionHandler extends \TYPO3\CMS\Core\Error\AbstractException
         * @return boolean
         */
        protected function discloseExceptionInformation(\Exception $exception) {
+               // Allow message to be shown in production mode if the exception is about
+               // trusted host configuration.  By doing so we do not disclose
+               // any valuable information to an attacker but avoid confusions among TYPO3 admins
+               // in production context.
+               if ($exception->getCode() === 1396795884) {
+                       return TRUE;
+               }
                // Show client error messages 40x in every case
                if ($exception instanceof \TYPO3\CMS\Core\Error\Http\AbstractClientErrorException) {
                        return TRUE;
@@ -134,4 +143,4 @@ class ProductionExceptionHandler extends \TYPO3\CMS\Core\Error\AbstractException
 }
 
 
-?>
\ No newline at end of file
+?>
index ff438d5..7e0a8c9 100644 (file)
@@ -98,7 +98,10 @@ abstract class AbstractStandaloneMessage extends \TYPO3\CMS\Core\Messaging\Abstr
                        '###CSS_CLASS###' => $classes[$this->severity],
                        '###TITLE###' => $this->title,
                        '###MESSAGE###' => $this->message,
-                       '###BASEURL###' => \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_URL'),
+                       // Avoid calling TYPO3_SITE_URL here to get the base URL as it might be that we output an exception message with
+                       // invalid trusted host, which would lead to a nested exception! See: #30377
+                       // Instead we calculate the relative path to the document root without involving HTTP request parameters.
+                       '###BASEURL###' => substr(PATH_site, strlen(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_DOCUMENT_ROOT'))),
                        '###TYPO3_mainDir###' => TYPO3_mainDir,
                        '###TYPO3_copyright_year###' => TYPO3_copyright_year
                );
@@ -155,4 +158,4 @@ abstract class AbstractStandaloneMessage extends \TYPO3\CMS\Core\Messaging\Abstr
 }
 
 
-?>
\ No newline at end of file
+?>
index 45452e5..585b03b 100644 (file)
@@ -48,6 +48,18 @@ class GeneralUtility {
        const SYSLOG_SEVERITY_WARNING = 2;
        const SYSLOG_SEVERITY_ERROR = 3;
        const SYSLOG_SEVERITY_FATAL = 4;
+
+       const ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL = '.*';
+       const ENV_TRUSTED_HOSTS_PATTERN_SERVER_NAME = 'SERVER_NAME';
+
+       /**
+        * State of host header value security check
+        * in order to avoid unnecessary multiple checks during one request
+        *
+        * @var bool
+        */
+       static protected $allowHostHeaderValue = FALSE;
+
        /**
         * Singleton instances returned by makeInstance, using the class names as
         * array keys
@@ -3185,6 +3197,7 @@ Connection: close
         *
         * @param string $getEnvName Name of the "environment variable"/"server variable" you wish to use. Valid values are SCRIPT_NAME, SCRIPT_FILENAME, REQUEST_URI, PATH_INFO, REMOTE_ADDR, REMOTE_HOST, HTTP_REFERER, HTTP_HOST, HTTP_USER_AGENT, HTTP_ACCEPT_LANGUAGE, QUERY_STRING, TYPO3_DOCUMENT_ROOT, TYPO3_HOST_ONLY, TYPO3_HOST_ONLY, TYPO3_REQUEST_HOST, TYPO3_REQUEST_URL, TYPO3_REQUEST_SCRIPT, TYPO3_REQUEST_DIR, TYPO3_SITE_URL, _ARRAY
         * @return string Value based on the input key, independent of server/os environment.
+        * @throws \UnexpectedValueException
         */
        static public function getIndpEnv($getEnvName) {
                /*
@@ -3327,6 +3340,12 @@ Connection: close
                                        $retVal = $host;
                                }
                        }
+                       if (!static::isAllowedHostHeaderValue($retVal)) {
+                               throw new \UnexpectedValueException(
+                                       'The current host header value does not match the configured trusted hosts pattern! Check the pattern defined in $GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'trustedHostsPattern\'] and adapt it, if you want to allow the current host header \'' . $retVal . '\' for your installation.',
+                                       1396795884
+                               );
+                       }
                        break;
                case 'HTTP_REFERER':
 
@@ -3454,6 +3473,51 @@ Connection: close
        }
 
        /**
+        * Checks if the provided host header value matches the trusted hosts pattern.
+        * If the pattern is not defined (which only can happen early in the bootstrap), deny any value.
+        *
+        * @param string $hostHeaderValue HTTP_HOST header value as sent during the request (may include port)
+        * @return bool
+        */
+       static public function isAllowedHostHeaderValue($hostHeaderValue) {
+               if (static::$allowHostHeaderValue === TRUE) {
+                       return TRUE;
+               }
+
+               // Allow all install tool requests
+               // We accept this risk to have the install tool always available
+               // Also CLI needs to be allowed as unfortunately AbstractUserAuthentication::getAuthInfoArray() accesses HTTP_HOST without reason on CLI
+               if (defined('TYPO3_REQUESTTYPE') && (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_INSTALL) || (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI)) {
+                       return static::$allowHostHeaderValue = TRUE;
+               }
+
+               // Deny the value if trusted host patterns is empty, which means we are early in the bootstrap
+               if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'])) {
+                       return FALSE;
+               }
+
+               if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] === self::ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL) {
+                       static::$allowHostHeaderValue = TRUE;
+               } elseif ($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] === self::ENV_TRUSTED_HOSTS_PATTERN_SERVER_NAME) {
+                       // Allow values that equal the server name
+                       // Note that this is only secure if name base virtual host are configured correctly in the webserver
+                       $defaultPort = self::getIndpEnv('TYPO3_SSL') ? '443' : '80';
+                       $parsedHostValue = parse_url('http://' . $hostHeaderValue);
+                       if (isset($parsedHostValue['port'])) {
+                               static::$allowHostHeaderValue = ($parsedHostValue['host'] === $_SERVER['SERVER_NAME'] && (string)$parsedHostValue['port'] === $_SERVER['SERVER_PORT']);
+                       } else {
+                               static::$allowHostHeaderValue = ($hostHeaderValue === $_SERVER['SERVER_NAME'] && $defaultPort === $_SERVER['SERVER_PORT']);
+                       }
+               } else {
+                       // In case name based virtual hosts are not possible, we allow setting a trusted host pattern
+                       // See https://typo3.org/teams/security/security-bulletins/typo3-core/typo3-core-sa-2014-001/ for further details
+                       static::$allowHostHeaderValue = (bool)preg_match('/^' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] . '$/', $hostHeaderValue);
+               }
+
+               return static::$allowHostHeaderValue;
+       }
+
+       /**
         * Gets the unixtime as milliseconds.
         *
         * @return integer The unixtime as milliseconds
index 6273608..459bdde 100644 (file)
@@ -81,7 +81,8 @@ return array(
                'cookieSecure' => 0,                                    // <p>Integer (0, 1, 2): Indicates that the cookie should only be transmitted over a secure HTTPS connection from the client.</p><dl><dt>0</dt><dd>always send cookie</dd><dt>1 (force HTTPS)</dt><dd>the cookie will only be set if a secure (HTTPS) connection exists - use this in combination with lockSSL since otherwise the application will fail and throw an exception</dd><dt>2</dt><dd>the cookie will be set in each case, but uses the secure flag if a secure (HTTPS) connection exists.</dd></dl>
                'cookieHttpOnly' => FALSE,                              // Boolean: When enabled the cookie will be made accessible only through the HTTP protocol. This means that the cookie won't be accessible by scripting languages, such as JavaScript. This setting can effectively help to reduce identity theft through XSS attacks (although it is not supported by all browsers).
                'doNotCheckReferer' => FALSE,                   // Boolean: If set, it's NOT checked numerous places that the refering host is the same as the current. This is an option you should set if you have problems with proxies not passing the HTTP_REFERER variable.
-               'recursiveDomainSearch' => FALSE,               // Boolean: If set, the search for domain records will be done recursively by stripping parts of the host name off until a matching domain record is found.
+               'recursiveDomainSearch' => FALSE,               // Boolean: If set, the search for domain records will be done recursively by stripping parts of the hostname off until a matching domain record is found.
+               'trustedHostsPattern' => 'SERVER_NAME', // String: Regular expression pattern that matches all allowed hostnames (including their ports) of this TYPO3 installation, or the string "SERVER_NAME" (default). The default value <code>SERVER_NAME</code> checks if the HTTP Host header equals the SERVER_NAME and SERVER_PORT. This is secure in correctly configured hosting environments and does not need further configuration. If you cannot change your hosting environment, you can enter a regular expression here. Examples: <code>.*\.domain\.com</code> matches all hosts that end with <code>.domain.com</code> with all corresponding subdomains. <code>(.*\.domain|.*\.otherdomain)\.com</code> matches all hostnames with subdomains from <code>.domain.com</code> and <code>.otherdomain.com</code>. Be aware that HTTP Host header may also contain a port. If your installation runs on a specific port, you need to explicitly allow this in your pattern, e.g. <code>www\.domain\.com:88</code> allows only <code>www.domain.com:88</code>, <strong>not</strong> <code>www.domain.com</code>. To disable this check completely (not recommended because it is <strong>insecure</strong>) you can use ".*" as pattern.
                'devIPmask' => '127.0.0.1,::1',                 // Defines a list of IP addresses which will allow development-output to display. The debug() function will use this as a filter. See the function \TYPO3\CMS\Core\Utility\GeneralUtility::cmpIP() for details on syntax. Setting this to blank value will deny all. Setting to "*" will allow all.
                'sqlDebug' => 0,                                                // <p>Integer (0, 1, 2). Allows displaying executed SQL queries in the browser (for debugging purposes and development)</p><dl><dt>0</dt><dd>no SQL shown (default)</dd><dt>1</dt><dd>show only failed queries</dd><dt>2</dt><dd>show all queries</dd></dl>
                'enable_DLOG' => FALSE,                                 // Boolean: Whether the developer log is enabled. See constant "TYPO3_DLOG"
@@ -688,7 +689,7 @@ return array(
                'proxy_password' => '',         // String: Default password.
                'proxy_auth_scheme' => 'basic',         // String: Default authentication method. Can either be "basic" or "digest". Defaults to "basic".
                'ssl_verify_peer' => FALSE,             // Boolean: Whether to verify peer's SSL certificate. Turned off by default, due to <a href="http://pear.php.net/manual/en/package.http.http-request2.adapters.php#package.http.http-request2.adapters.socket" target="_blank">issues with Socket adapter</a>. You are advised to use the <em>curl</em> adapter and enable this option!
-               'ssl_verify_host' => TRUE,              // Boolean: Whether to check that Common Name in SSL certificate matches host name. There are some <a href="http://pear.php.net/manual/en/package.http.http-request2.adapters.php#package.http.http-request2.adapters.socket" target="_blank">issues with Socket Adapter</a>.
+               'ssl_verify_host' => TRUE,              // Boolean: Whether to check that Common Name in SSL certificate matches hostname. There are some <a href="http://pear.php.net/manual/en/package.http.http-request2.adapters.php#package.http.http-request2.adapters.socket" target="_blank">issues with Socket Adapter</a>.
                'ssl_cafile' => '',             // String: Certificate Authority file to verify the peer with (use when ssl_verify_peer is TRUE).
                'ssl_capath' => '',             // String: Directory holding multiple Certificate Authority files.
                'ssl_local_cert' => '',         // String: Name of a file containing local certificate.
diff --git a/typo3/sysext/core/Tests/Unit/Utility/Fixtures/GeneralUtilityFixture.php b/typo3/sysext/core/Tests/Unit/Utility/Fixtures/GeneralUtilityFixture.php
new file mode 100644 (file)
index 0000000..4ebc2a5
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+namespace TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2014 Helmut Hummel <helmut.hummel@typo3.org>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the text file GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+/**
+ * Class GeneralUtilityFixture
+ */
+class GeneralUtilityFixture extends GeneralUtility {
+
+       /**
+        * @var int
+        */
+       public static $isAllowedHostHeaderValueCallCount = 0;
+
+       /**
+        * Tracks number of calls done to this method
+        *
+        * @param string $hostHeaderValue Host name without port
+        * @return bool
+        */
+       static public function isAllowedHostHeaderValue($hostHeaderValue) {
+               self::$isAllowedHostHeaderValueCallCount++;
+               return TRUE;
+       }
+
+       /**
+        * @param boolean $allowHostHeaderValue
+        */
+       static public function setAllowHostHeaderValue($allowHostHeaderValue) {
+               static::$allowHostHeaderValue = $allowHostHeaderValue;
+       }
+
+
+}
\ No newline at end of file
index 13504f1..f18c1a4 100644 (file)
@@ -1,6 +1,5 @@
 <?php
 namespace TYPO3\CMS\Core\Tests\Unit\Utility;
-use TYPO3\CMS\Core\Utility;
 
 /***************************************************************
  *  Copyright notice
@@ -25,6 +24,9 @@ use TYPO3\CMS\Core\Utility;
  *  This copyright notice MUST APPEAR in all copies of the script!
  ***************************************************************/
 
+use TYPO3\CMS\Core\Utility;
+use TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\GeneralUtilityFixture;
+
 /**
  * Testcase for class \TYPO3\CMS\Core\Utility\GeneralUtility
  *
@@ -47,6 +49,9 @@ class GeneralUtilityTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        protected $singletonInstances = array();
 
        public function setUp() {
+               GeneralUtilityFixture::$isAllowedHostHeaderValueCallCount = 0;
+               GeneralUtilityFixture::setAllowHostHeaderValue(FALSE);
+               $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = Utility\GeneralUtility::ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL;
                $this->singletonInstances = Utility\GeneralUtility::getSingletonInstances();
        }
 
@@ -1564,6 +1569,175 @@ class GeneralUtilityTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 
        /**
         * @test
+        */
+       public function isAllowedHostHeaderValueReturnsFalseIfTrusedHostsIsNotConfigured() {
+               unset($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern']);
+               $this->assertFalse(Utility\GeneralUtility::isAllowedHostHeaderValue('evil.foo.bar'));
+       }
+
+       /**
+        * @return array
+        */
+       static public function hostnamesMatchingTrustedHostsConfigurationDataProvider() {
+               return array(
+                       'hostname without port matching' => array('lolli.did.this', '.*\.did\.this'),
+                       'other hostname without port matching' => array('helmut.did.this', '.*\.did\.this'),
+                       'two different hostnames without port matching 1st host' => array('helmut.is.secure', '(helmut\.is\.secure|lolli\.is\.secure)'),
+                       'two different hostnames without port matching 2nd host' => array('lolli.is.secure', '(helmut\.is\.secure|lolli\.is\.secure)'),
+                       'hostname with port matching' => array('lolli.did.this:42', '.*\.did\.this:42'),
+               );
+       }
+
+       /**
+        * @return array
+        */
+       static public function hostnamesNotMatchingTrustedHostsConfigurationDataProvider() {
+               return array(
+                       'hostname without port' => array('lolli.did.this', 'helmut\.did\.this'),
+                       'hostname with port, but port not allowed' => array('lolli.did.this:42', 'helmut\.did\.this'),
+                       'two different hostnames in pattern but host header starts with differnet value #1' => array('sub.helmut.is.secure', '(helmut\.is\.secure|lolli\.is\.secure)'),
+                       'two different hostnames in pattern but host header starts with differnet value #2' => array('sub.lolli.is.secure', '(helmut\.is\.secure|lolli\.is\.secure)'),
+                       'two different hostnames in pattern but host header ends with differnet value #1' => array('helmut.is.secure.tld', '(helmut\.is\.secure|lolli\.is\.secure)'),
+                       'two different hostnames in pattern but host header ends with differnet value #2' => array('lolli.is.secure.tld', '(helmut\.is\.secure|lolli\.is\.secure)'),
+               );
+       }
+
+       /**
+        * @param string $httpHost HTTP_HOST string
+        * @param string $hostNamePattern trusted hosts pattern
+        * @test
+        * @dataProvider hostnamesMatchingTrustedHostsConfigurationDataProvider
+        */
+       public function isAllowedHostHeaderValueReturnsTrueIfHostValueMatches($httpHost, $hostNamePattern) {
+               $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = $hostNamePattern;
+               $this->assertTrue(Utility\GeneralUtility::isAllowedHostHeaderValue($httpHost));
+       }
+
+       /**
+        * @param string $httpHost HTTP_HOST string
+        * @param string $hostNamePattern trusted hosts pattern
+        * @test
+        * @dataProvider hostnamesNotMatchingTrustedHostsConfigurationDataProvider
+        */
+       public function isAllowedHostHeaderValueReturnsFalseIfHostValueMatches($httpHost, $hostNamePattern) {
+               $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = $hostNamePattern;
+               $this->assertFalse(Utility\GeneralUtility::isAllowedHostHeaderValue($httpHost));
+       }
+
+       public function serverNamePatternDataProvider() {
+               return array(
+                       'host value matches server name and server port is default http' => array(
+                               'httpHost' => 'secure.web.server',
+                               'serverName' => 'secure.web.server',
+                               'isAllowed' => TRUE,
+                               'serverPort' => '80',
+                               'ssl' => 'Off',
+                       ),
+                       'host value matches server name and server port is default https' => array(
+                               'httpHost' => 'secure.web.server',
+                               'serverName' => 'secure.web.server',
+                               'isAllowed' => TRUE,
+                               'serverPort' => '443',
+                               'ssl' => 'On',
+                       ),
+                       'host value matches server name and server port' => array(
+                               'httpHost' => 'secure.web.server:88',
+                               'serverName' => 'secure.web.server',
+                               'isAllowed' => TRUE,
+                               'serverPort' => '88',
+                       ),
+                       'host value is ipv6 but matches server name and server port' => array(
+                               'httpHost' => '[::1]:81',
+                               'serverName' => '[::1]',
+                               'isAllowed' => TRUE,
+                               'serverPort' => '81',
+                       ),
+                       'host value does not match server name' => array(
+                               'httpHost' => 'insecure.web.server',
+                               'serverName' => 'secure.web.server',
+                               'isAllowed' => FALSE,
+                       ),
+                       'host value does not match server port' => array(
+                               'httpHost' => 'secure.web.server:88',
+                               'serverName' => 'secure.web.server',
+                               'isAllowed' => FALSE,
+                               'serverPort' => '89',
+                       ),
+                       'host value has default port that does not match server port' => array(
+                               'httpHost' => 'secure.web.server',
+                               'serverName' => 'secure.web.server',
+                               'isAllowed' => FALSE,
+                               'serverPort' => '81',
+                               'ssl' => 'Off',
+                       ),
+                       'host value has default port that does not match server ssl port' => array(
+                               'httpHost' => 'secure.web.server',
+                               'serverName' => 'secure.web.server',
+                               'isAllowed' => FALSE,
+                               'serverPort' => '444',
+                               'ssl' => 'On',
+                       ),
+               );
+       }
+
+       /**
+        * @param string $httpHost
+        * @param string $serverName
+        * @param bool $isAllowed
+        * @param string $serverPort
+        * @param string $ssl
+        *
+        * @test
+        * @dataProvider serverNamePatternDataProvider
+        */
+       public function isAllowedHostHeaderValueWorksCorrectlyWithWithServerNamePattern($httpHost, $serverName, $isAllowed, $serverPort = '80', $ssl = 'Off') {
+               $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = Utility\GeneralUtility::ENV_TRUSTED_HOSTS_PATTERN_SERVER_NAME;
+               $_SERVER['SERVER_NAME'] = $serverName;
+               $_SERVER['SERVER_PORT'] = $serverPort;
+               $_SERVER['HTTPS'] = $ssl;
+               $this->assertSame($isAllowed, Utility\GeneralUtility::isAllowedHostHeaderValue($httpHost));
+       }
+
+       /**
+        * @test
+        */
+       public function allGetIndpEnvCallsRelatedToHostNamesCallIsAllowedHostHeaderValue() {
+               GeneralUtilityFixture::getIndpEnv('HTTP_HOST');
+               GeneralUtilityFixture::getIndpEnv('TYPO3_HOST_ONLY');
+               GeneralUtilityFixture::getIndpEnv('TYPO3_REQUEST_HOST');
+               GeneralUtilityFixture::getIndpEnv('TYPO3_REQUEST_URL');
+               $this->assertSame(4, GeneralUtilityFixture::$isAllowedHostHeaderValueCallCount);
+       }
+
+       /**
+        * @param string $httpHost HTTP_HOST string
+        * @param string $hostNamePattern trusted hosts pattern
+        * @test
+        * @dataProvider hostnamesNotMatchingTrustedHostsConfigurationDataProvider
+        * @expectedException \UnexpectedValueException
+        * @expectedExceptionCode 1396795884
+        */
+       public function getIndpEnvForHostThrowsExceptionForNotAllowedHostnameValues($httpHost, $hostNamePattern) {
+               $_SERVER['HTTP_HOST'] = $httpHost;
+               $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = $hostNamePattern;
+               Utility\GeneralUtility::getIndpEnv('HTTP_HOST');
+       }
+
+       /**
+        * @param string $httpHost HTTP_HOST string
+        * @param string $hostNamePattern trusted hosts pattern (not used in this test currently)
+        * @test
+        * @dataProvider hostnamesNotMatchingTrustedHostsConfigurationDataProvider
+        */
+       public function getIndpEnvForHostAllowsAllHostnameValuesOfDefaultConfiguration($httpHost, $hostNamePattern) {
+               $_SERVER['HTTP_HOST'] = $httpHost;
+               // DefaultConfiguration is currently applied for tests. In case this is changed, this test needs to be adapted.
+               // $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = '/.*/';
+               $this->assertSame($httpHost, Utility\GeneralUtility::getIndpEnv('HTTP_HOST'));
+       }
+
+       /**
+        * @test
         * @dataProvider hostnameAndPortDataProvider
         */
        public function getIndpEnvTypo3PortParsesHostnamesAndIpAdresses($httpHost, $dummy, $expectedPort) {
@@ -1791,6 +1965,7 @@ class GeneralUtilityTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
         * @return array Valid url
         */
        public function sanitizeLocalUrlValidUrlDataProvider() {
+               $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = Utility\GeneralUtility::ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL;
                $subDirectory = Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
                $typo3SiteUrl = Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_URL');
                $typo3RequestHost = Utility\GeneralUtility::getIndpEnv('TYPO3_REQUEST_HOST');