From 573e752f043912e74cfa48508f6efc15725427c1 Mon Sep 17 00:00:00 2001 From: effulgentsia <alex.bronstein@acquia.com> Date: Fri, 14 May 2021 13:39:12 -0700 Subject: [PATCH] Issue #3209628 by longwave, rachel_norfolk, antiorario, phenaproxima, rootwork, larowlan, neclimdul, Maeglin: Add Permissions-Policy header to block Google FLoC --- .../scaffold/files/default.settings.php | 15 ++++ .../FinishResponseSubscriber.php | 14 +++ .../Core/Http/BlockInterestCohortTest.php | 85 +++++++++++++++++++ sites/default/default.settings.php | 15 ++++ 4 files changed, 129 insertions(+) create mode 100644 core/tests/Drupal/KernelTests/Core/Http/BlockInterestCohortTest.php diff --git a/core/assets/scaffold/files/default.settings.php b/core/assets/scaffold/files/default.settings.php index 0165492a545e..f02935a27abc 100644 --- a/core/assets/scaffold/files/default.settings.php +++ b/core/assets/scaffold/files/default.settings.php @@ -611,6 +611,21 @@ # ini_set('pcre.backtrack_limit', 200000); # ini_set('pcre.recursion_limit', 200000); +/** + * Add Permissions-Policy header to disable Google FLoC. + * + * By default, Drupal sends the 'Permissions-Policy: interest-cohort=()' header + * to disable Google's Federated Learning of Cohorts feature, introduced in + * Chrome 89. + * + * See https://en.wikipedia.org/wiki/Federated_Learning_of_Cohorts for more + * information about FLoC. + * + * If you don't wish to disable FLoC in Chrome, you can set this value + * to FALSE. + */ +# $settings['block_interest_cohort'] = TRUE; + /** * Configuration overrides. * diff --git a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php index c8d76eb294dc..d5816315a1cb 100644 --- a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php @@ -131,6 +131,20 @@ public function onRespond(ResponseEvent $event) { $response->headers->set('X-Content-Type-Options', 'nosniff', FALSE); $response->headers->set('X-Frame-Options', 'SAMEORIGIN', FALSE); + // Add a Permissions-Policy header to block Federated Learning of Cohorts. + if (Settings::get('block_interest_cohort', TRUE)) { + if (!$response->headers->has('Permissions-Policy')) { + $response->headers->set('Permissions-Policy', 'interest-cohort=()'); + } + else { + // Only add interest-cohort if the header does not contain it already. + $permissions_policy = $response->headers->get('Permissions-Policy'); + if (strpos($permissions_policy, 'interest-cohort') === FALSE) { + $response->headers->set('Permissions-Policy', $permissions_policy . ', interest-cohort=()'); + } + } + } + // If the current response isn't an implementation of the // CacheableResponseInterface, we assume that a Response is either // explicitly not cacheable or that caching headers are already set in diff --git a/core/tests/Drupal/KernelTests/Core/Http/BlockInterestCohortTest.php b/core/tests/Drupal/KernelTests/Core/Http/BlockInterestCohortTest.php new file mode 100644 index 000000000000..ff87ae470d1c --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Http/BlockInterestCohortTest.php @@ -0,0 +1,85 @@ +<?php + +namespace Drupal\KernelTests\Core\Http; + +use Drupal\Core\Site\Settings; +use Drupal\KernelTests\KernelTestBase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\ResponseEvent; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * Tests the Permissions-Policy header added by FinishResponseSubscriber. + * + * @group Http + */ +class BlockInterestCohortTest extends KernelTestBase { + + /** + * Tests that FLoC is blocked by default. + */ + public function testDefaultBlocking() { + $request = Request::create('/'); + $response = \Drupal::service('http_kernel')->handle($request); + + $this->assertSame('interest-cohort=()', $response->headers->get('Permissions-Policy')); + } + + /** + * Tests that an existing interest-cohort policy is not overwritten. + */ + public function testExistingInterestCohortPolicy() { + $headers['Permissions-Policy'] = 'interest-cohort=*'; + + $kernel = \Drupal::service('http_kernel'); + $request = Request::create('/'); + $response = new Response('', 200, $headers); + $event = new ResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST, $response); + \Drupal::service('finish_response_subscriber')->onRespond($event); + + $this->assertSame($headers['Permissions-Policy'], $response->headers->get('Permissions-Policy')); + } + + /** + * Tests that an existing header is appended to correctly. + */ + public function testExistingPolicyHeader() { + $headers['Permissions-Policy'] = 'geolocation=()'; + + $kernel = \Drupal::service('http_kernel'); + $request = Request::create('/'); + $response = new Response('', 200, $headers); + $event = new ResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST, $response); + \Drupal::service('finish_response_subscriber')->onRespond($event); + + $permissions_policy = $response->headers->get('Permissions-Policy'); + $this->assertStringContainsString('geolocation=()', $permissions_policy); + $this->assertStringContainsString('interest-cohort=()', $permissions_policy); + } + + /** + * Tests that FLoC blocking is ignored for subrequests. + */ + public function testSubrequestBlocking() { + $request = Request::create('/'); + $response = \Drupal::service('http_kernel')->handle($request, HttpKernelInterface::SUB_REQUEST); + + $this->assertFalse($response->headers->has('Permissions-Policy')); + } + + /** + * Tests that FLoC blocking can be disabled in settings.php. + */ + public function testDisableBlockSetting() { + $settings = Settings::getAll(); + $settings['block_interest_cohort'] = FALSE; + new Settings($settings); + + $request = Request::create('/'); + $response = \Drupal::service('http_kernel')->handle($request); + + $this->assertFalse($response->headers->has('Permissions-Policy')); + } + +} diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index 0165492a545e..f02935a27abc 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -611,6 +611,21 @@ # ini_set('pcre.backtrack_limit', 200000); # ini_set('pcre.recursion_limit', 200000); +/** + * Add Permissions-Policy header to disable Google FLoC. + * + * By default, Drupal sends the 'Permissions-Policy: interest-cohort=()' header + * to disable Google's Federated Learning of Cohorts feature, introduced in + * Chrome 89. + * + * See https://en.wikipedia.org/wiki/Federated_Learning_of_Cohorts for more + * information about FLoC. + * + * If you don't wish to disable FLoC in Chrome, you can set this value + * to FALSE. + */ +# $settings['block_interest_cohort'] = TRUE; + /** * Configuration overrides. * -- GitLab