Issue #2417075 by tstoeckler, David4514, dawehner, sachbearbeiter, mpdonadio: Trusted host verification is incompatible with URIs with the "internal" scheme
parent
6238aabe9e
commit
0a84d315ab
|
@ -20,6 +20,7 @@ use Drupal\Core\DependencyInjection\ServiceProviderInterface;
|
|||
use Drupal\Core\DependencyInjection\YamlFileLoader;
|
||||
use Drupal\Core\Extension\ExtensionDiscovery;
|
||||
use Drupal\Core\File\MimeType\MimeTypeGuesser;
|
||||
use Drupal\Core\Http\TrustedHostsRequestFactory;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\PageCache\RequestPolicyInterface;
|
||||
use Drupal\Core\PhpStorage\PhpStorageFactory;
|
||||
|
@ -1297,13 +1298,24 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
|||
* TRUE if the Host header is trusted, FALSE otherwise.
|
||||
*
|
||||
* @see https://www.drupal.org/node/1992030
|
||||
* @see \Drupal\Core\Http\TrustedHostsRequestFactory
|
||||
*/
|
||||
protected static function setupTrustedHosts(Request $request, $host_patterns) {
|
||||
$request->setTrustedHosts($host_patterns);
|
||||
|
||||
// Get the host, which will validate the current request.
|
||||
try {
|
||||
$request->getHost();
|
||||
$host = $request->getHost();
|
||||
|
||||
// Fake requests created through Request::create() without passing in the
|
||||
// server variables from the main request have a default host of
|
||||
// 'localhost'. If 'localhost' does not match any of the trusted host
|
||||
// patterns these fake requests would fail the host verification. Instead,
|
||||
// TrustedHostsRequestFactory makes sure to pass in the server variables
|
||||
// from the main request.
|
||||
$request_factory = new TrustedHostsRequestFactory($host);
|
||||
Request::setFactory([$request_factory, 'createRequest']);
|
||||
|
||||
}
|
||||
catch (\UnexpectedValueException $e) {
|
||||
return FALSE;
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\Core\Http\TrustedHostsRequestFactory.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Http;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Provides a request factory for requests using host verification.
|
||||
*
|
||||
* Because the trusted host patterns for requests are stored statically, they
|
||||
* are consulted even for fake request created with Request::create(), whose
|
||||
* host is 'localhost' by default. Such requests would fail host verification
|
||||
* unless 'localhost' matches one of the trusted host patterns. To circumvent
|
||||
* this problem, this factory injects the server variables from the main request
|
||||
* into each newly created request, so that the host is correctly set even for
|
||||
* fake requests and they properly pass host verification.
|
||||
*
|
||||
* @see \Drupal\Core\DrupalKernel::setupTrustedHosts()
|
||||
*/
|
||||
class TrustedHostsRequestFactory {
|
||||
|
||||
/**
|
||||
* The host of the main request.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $host;
|
||||
|
||||
/**
|
||||
* Creates a new TrustedHostsRequestFactory.
|
||||
*
|
||||
* @param string $host
|
||||
* The host of the main request.
|
||||
*/
|
||||
public function __construct($host) {
|
||||
$this->host = (string) $host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new request object.
|
||||
*
|
||||
* @param array $query
|
||||
* (optional) The query (GET) or request (POST) parameters.
|
||||
* @param array $request
|
||||
* (optional) An array of request variables.
|
||||
* @param array $attributes
|
||||
* (optioanl) An array of attributes.
|
||||
* @param array $cookies
|
||||
* (optional) The request cookies ($_COOKIE).
|
||||
* @param array $files
|
||||
* (optional) The request files ($_FILES).
|
||||
* @param array $server
|
||||
* (optional) The server parameters ($_SERVER).
|
||||
* @param string $content
|
||||
* (optional) The raw body data.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Request
|
||||
* A new request object.
|
||||
**/
|
||||
public function createRequest(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) {
|
||||
if (empty($server['HTTP_HOST']) || ($server['HTTP_HOST'] === 'localhost' && $this->host !== 'localhost')) {
|
||||
$server['HTTP_HOST'] = $this->host;
|
||||
}
|
||||
return new Request($query, $request, $attributes, $cookies, $files, $server, $content);
|
||||
}
|
||||
|
||||
}
|
|
@ -8,7 +8,10 @@
|
|||
namespace Drupal\system\Tests\System;
|
||||
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Tests output on the status overview page.
|
||||
|
@ -59,4 +62,55 @@ class TrustedHostsTest extends WebTestBase {
|
|||
$this->assertRaw(t('The trusted_host_patterns setting is set to allow'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that fake requests have the proper host configured.
|
||||
*
|
||||
* @see \Drupal\Core\Http\TrustedHostsRequestFactory
|
||||
*/
|
||||
public function testFakeRequests() {
|
||||
$this->container->get('module_installer')->install(['trusted_hosts_test']);
|
||||
|
||||
$host = $this->container->get('request_stack')->getCurrentRequest()->getHost();
|
||||
$settings['settings']['trusted_host_patterns'] = (object) array(
|
||||
'value' => array('^' . preg_quote($host) . '$'),
|
||||
'required' => TRUE,
|
||||
);
|
||||
|
||||
$this->writeSettings($settings);
|
||||
|
||||
$this->drupalGet('trusted-hosts-test/fake-request');
|
||||
$this->assertText('Host: ' . $host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that shortcut module works together with host verification.
|
||||
*/
|
||||
public function testShortcut() {
|
||||
$this->container->get('module_installer')->install(['block', 'shortcut']);
|
||||
$this->rebuildContainer();
|
||||
|
||||
/** @var \Drupal\Core\Entity\EntityManagerInterface $entity_manager */
|
||||
$entity_manager = $this->container->get('entity.manager');
|
||||
$shortcut_storage = $entity_manager->getStorage('shortcut');
|
||||
|
||||
$shortcut = $shortcut_storage->create([
|
||||
'title' => $this->randomString(),
|
||||
'link' => 'internal:/admin/reports/status',
|
||||
'shortcut_set' => 'default',
|
||||
]);
|
||||
$shortcut_storage->save($shortcut);
|
||||
|
||||
// Grant the current user access to see the shortcuts.
|
||||
$role_storage = $entity_manager->getStorage('user_role');
|
||||
$roles = $this->loggedInUser->getRoles(TRUE);
|
||||
/** @var \Drupal\user\RoleInterface $role */
|
||||
$role = $role_storage->load(reset($roles));
|
||||
$role->grantPermission('access shortcuts')->save();
|
||||
|
||||
$this->drupalPlaceBlock('shortcuts');
|
||||
|
||||
$this->drupalGet('');
|
||||
$this->assertLink($shortcut->label());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\trusted_hosts_test\Controller\TrustedHostsTestController.
|
||||
*/
|
||||
|
||||
namespace Drupal\trusted_hosts_test\Controller;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Provides a test controller for testing the trusted hosts setting.
|
||||
*/
|
||||
class TrustedHostsTestController {
|
||||
|
||||
/**
|
||||
* Creates a fake request and prints out its host.
|
||||
*/
|
||||
public function fakeRequestHost() {
|
||||
$request = Request::create('/');
|
||||
return ['#markup' => 'Host: ' . $request->getHost()];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
type: module
|
||||
name: 'Trusted hosts test module'
|
||||
core: 8.x
|
||||
package: Testing
|
||||
version: VERSION
|
|
@ -0,0 +1,6 @@
|
|||
trusted_hosts_test.fake_request:
|
||||
path: '/trusted-hosts-test/fake-request'
|
||||
defaults:
|
||||
_controller: 'Drupal\trusted_hosts_test\Controller\TrustedHostsTestController::fakeRequestHost'
|
||||
requirements:
|
||||
_access: 'TRUE'
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\Tests\Core\Http\TrustedHostsRequestFactoryTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Core\Http;
|
||||
use Drupal\Core\Http\TrustedHostsRequestFactory;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Tests the trusted hosts request factory.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\Core\Http\TrustedHostsRequestFactory
|
||||
* @group Http
|
||||
*/
|
||||
class TrustedHostsRequestFactoryTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests TrustedHostsRequestFactory::createRequest().
|
||||
*
|
||||
* @param string $host
|
||||
* The host to pass into TrustedHostsRequestFactory.
|
||||
* @param array $server
|
||||
* The server array to pass into
|
||||
* TrustedHostsRequestFactory::createRequest().
|
||||
* @param string $expected
|
||||
* The expected host of the created request.
|
||||
*
|
||||
* @covers ::createRequest
|
||||
* @dataProvider providerTestCreateRequest
|
||||
*/
|
||||
public function testCreateRequest($host, $server, $expected) {
|
||||
$request_factory = new TrustedHostsRequestFactory($host);
|
||||
$request = $request_factory->createRequest([], [], [], [], [], $server, []);
|
||||
$this->assertEquals($expected, $request->getHost());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for testCreateRequest().
|
||||
*
|
||||
* @return array
|
||||
* An array of test cases, where each test case is an array with the
|
||||
* following values:
|
||||
* - A string containing the host to pass into TrustedHostsRequestFactory.
|
||||
* - An array containing the server array to pass into
|
||||
* TrustedHostsRequestFactory::createRequest().
|
||||
* - A string containing the expected host of the created request.
|
||||
*/
|
||||
public function providerTestCreateRequest() {
|
||||
$tests = [];
|
||||
$tests[] = ['example.com', [], 'example.com'];
|
||||
$tests[] = ['localhost', [], 'localhost'];
|
||||
$tests[] = ['localhost', ['HTTP_HOST' => 'localhost'], 'localhost'];
|
||||
$tests[] = ['example.com', ['HTTP_HOST' => 'localhost'], 'example.com'];
|
||||
return $tests;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue