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 @@ $settings['update_free_access'] = FALSE; # 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 @@ class FinishResponseSubscriber implements EventSubscriberInterface { $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 @@ +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 @@ $settings['update_free_access'] = FALSE; # 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. *