diff --git a/.htaccess b/.htaccess index 5248bd85d61936b9f090993f396af1c31acbd3b5..7db5d3e5786887f4a4e1b28dba245dbe346d5e09 100644 --- a/.htaccess +++ b/.htaccess @@ -165,3 +165,9 @@ DirectoryIndex index.php index.html index.htm </FilesMatch> </IfModule> </IfModule> + +# Add headers to all responses. +<IfModule mod_headers.c> + # Disable content sniffing, since it's an attack vector. + Header always set X-Content-Type-Options nosniff +</IfModule> diff --git a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php index fb68b07a04252d57b07ac7f63eeb79fc28c2f626..963795ed5185d02a26faf17545b3f8e60d202a82 100644 --- a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php @@ -96,6 +96,12 @@ public function onRespond(FilterResponseEvent $event) { // Set the Content-language header. $response->headers->set('Content-language', $this->languageManager->getCurrentLanguage()->getId()); + // Prevent browsers from sniffing a response and picking a MIME type + // different from the declared content-type, since that can lead to + // XSS and other vulnerabilities. + // https://www.owasp.org/index.php/List_of_useful_HTTP_headers + $response->headers->set('X-Content-Type-Options', 'nosniff', FALSE); + // Attach globally-declared headers to the response object so that Symfony // can send them for us correctly. // @todo Remove this once drupal_process_attached() no longer calls diff --git a/core/modules/system/src/Tests/Routing/RouterTest.php b/core/modules/system/src/Tests/Routing/RouterTest.php index 5651768a6d006f21f7d370155ac278cd15955eb2..e3287595e539db68eacc12e56b85f088407af093 100644 --- a/core/modules/system/src/Tests/Routing/RouterTest.php +++ b/core/modules/system/src/Tests/Routing/RouterTest.php @@ -25,17 +25,19 @@ class RouterTest extends WebTestBase { public static $modules = array('block', 'router_test'); /** - * Confirms that the router can get to a controller. + * Confirms that our default controller logic works properly. */ - public function testCanRoute() { + public function testDefaultController() { + // Confirm that the router can get to a controller. $this->drupalGet('router_test/test1'); $this->assertRaw('test1', 'The correct string was returned because the route was successful.'); - } - /** - * Confirms that our default controller logic works properly. - */ - public function testDefaultController() { + // Check expected headers from FinishResponseSubscriber + $headers = $this->drupalGetHeaders(); + $this->assertEqual($headers['x-ua-compatible'], 'IE=edge,chrome=1'); + $this->assertEqual($headers['content-language'], 'en'); + $this->assertEqual($headers['x-content-type-options'], 'nosniff'); + $this->drupalGet('router_test/test2'); $this->assertRaw('test2', 'The correct string was returned because the route was successful.');