Skip to content

Commit 33c79ce

Browse files
committed
feature #49306 [Security] Add logout configuration for Clear-Site-Data header (maxbeckers)
This PR was merged into the 6.3 branch. Discussion ---------- [Security] Add logout configuration for Clear-Site-Data header | Q | A | ------------- | --- | Branch? | 6.3 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | Fix #49266 | License | MIT | Doc PR | symfony/symfony-docs#17900 Enhance security by issuing a Clear-Site-Data header on logout. * [Clear-Site-Data](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data#sign_out_of_a_web_site) Documentation * Example: https://www.w3.org/TR/clear-site-data/#example-signout Default config is off. Config example for all: ```yaml security: # ... firewalls: main: # ... logout: path: app_logout clear_site_data: - "*" ``` Instead of all with the ``*`` it's also possible to add a set of ``cache``, ``cookies``, ``storage``, ``executionContexts``. For example without cookies it will look like this: ```yaml security: # ... firewalls: main: # ... logout: path: app_logout clear_site_data: - cache - storage - executionContexts ``` **TODO** - [x] Doc PR symfony/symfony-docs#17900 Commits ------- f9e76c1e47 [Security] Add logout configuration for Clear-Site-Data header
2 parents f5538ac + c9cebff commit 33c79ce

File tree

9 files changed

+138
-2
lines changed

9 files changed

+138
-2
lines changed

DependencyInjection/MainConfiguration.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,15 @@ private function addFirewallsSection(ArrayNodeDefinition $rootNode, array $facto
251251
->scalarNode('path')->defaultValue('/logout')->end()
252252
->scalarNode('target')->defaultValue('/')->end()
253253
->booleanNode('invalidate_session')->defaultTrue()->end()
254+
->arrayNode('clear_site_data')
255+
->performNoDeepMerging()
256+
->beforeNormalization()->ifString()->then(fn ($v) => $v ? array_map('trim', explode(',', $v)) : [])->end()
257+
->enumPrototype()
258+
->values([
259+
'*', 'cache', 'cookies', 'storage', 'executionContexts',
260+
])
261+
->end()
262+
->end()
254263
->end()
255264
->fixXmlConfig('delete_cookie')
256265
->children()

DependencyInjection/SecurityExtension.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,13 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
479479
->addTag('kernel.event_subscriber', ['dispatcher' => $firewallEventDispatcherId]);
480480
}
481481

482+
// add clear site data listener
483+
if ($firewall['logout']['clear_site_data'] ?? false) {
484+
$container->setDefinition('security.logout.listener.clear_site_data.'.$id, new ChildDefinition('security.logout.listener.clear_site_data'))
485+
->addArgument($firewall['logout']['clear_site_data'])
486+
->addTag('kernel.event_subscriber', ['dispatcher' => $firewallEventDispatcherId]);
487+
}
488+
482489
// register with LogoutUrlGenerator
483490
$container
484491
->getDefinition('security.logout_url_generator')

Resources/config/schema/security-1.0.xsd

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,10 @@
171171
</xsd:complexType>
172172

173173
<xsd:complexType name="logout">
174-
<xsd:sequence>
174+
<xsd:choice minOccurs="0" maxOccurs="unbounded">
175175
<xsd:element name="delete-cookie" type="delete_cookie" minOccurs="0" maxOccurs="unbounded" />
176-
</xsd:sequence>
176+
<xsd:element name="clear-site-data" type="clear_site_data" minOccurs="0" maxOccurs="unbounded" />
177+
</xsd:choice>
177178
<xsd:attribute name="csrf-parameter" type="xsd:string" />
178179
<xsd:attribute name="csrf-token-generator" type="xsd:string" />
179180
<xsd:attribute name="csrf-token-id" type="xsd:string" />
@@ -407,4 +408,14 @@
407408
</xsd:simpleContent>
408409
</xsd:complexType>
409410

411+
<xsd:simpleType name="clear_site_data">
412+
<xsd:restriction base="xsd:string">
413+
<xsd:enumeration value="*" />
414+
<xsd:enumeration value="cache" />
415+
<xsd:enumeration value="cookies" />
416+
<xsd:enumeration value="storage" />
417+
<xsd:enumeration value="executionContexts" />
418+
</xsd:restriction>
419+
</xsd:simpleType>
420+
410421
</xsd:schema>

Resources/config/security_listeners.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\Component\Security\Http\Authentication\CustomAuthenticationSuccessHandler;
1818
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler;
1919
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler;
20+
use Symfony\Component\Security\Http\EventListener\ClearSiteDataLogoutListener;
2021
use Symfony\Component\Security\Http\EventListener\CookieClearingLogoutListener;
2122
use Symfony\Component\Security\Http\EventListener\DefaultLogoutListener;
2223
use Symfony\Component\Security\Http\EventListener\SessionLogoutListener;
@@ -64,6 +65,9 @@
6465
->set('security.logout.listener.session', SessionLogoutListener::class)
6566
->abstract()
6667

68+
->set('security.logout.listener.clear_site_data', ClearSiteDataLogoutListener::class)
69+
->abstract()
70+
6771
->set('security.logout.listener.cookie_clearing', CookieClearingLogoutListener::class)
6872
->abstract()
6973

Tests/DependencyInjection/CompleteConfigurationTestCase.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ public function testFirewalls()
181181
'invalidate_session' => true,
182182
'delete_cookies' => [],
183183
'enable_csrf' => null,
184+
'clear_site_data' => [],
184185
],
185186
],
186187
[
@@ -708,6 +709,13 @@ public function testFirewallListenerWithProvider()
708709
$this->addToAssertionCount(1);
709710
}
710711

712+
public function testFirewallLogoutClearSiteData()
713+
{
714+
$container = $this->getContainer('logout_clear_site_data');
715+
$ClearSiteDataConfig = $container->getDefinition('security.firewall.map.config.main')->getArgument(12)['clear_site_data'];
716+
$this->assertSame(['cookies', 'executionContexts'], $ClearSiteDataConfig);
717+
}
718+
711719
protected function getContainer($file)
712720
{
713721
$file .= '.'.$this->getFileExtension();
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
$container->loadFromExtension('security', [
4+
'providers' => [
5+
'default' => ['id' => 'foo'],
6+
],
7+
8+
'firewalls' => [
9+
'main' => [
10+
'provider' => 'default',
11+
'form_login' => true,
12+
'logout' => [
13+
'clear-site-data' => [
14+
'cookies',
15+
'executionContexts',
16+
],
17+
],
18+
],
19+
],
20+
]);
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<srv:container xmlns="http://symfony.com/schema/dic/security"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xmlns:srv="http://symfony.com/schema/dic/services"
6+
xsi:schemaLocation="http://symfony.com/schema/dic/services
7+
https://symfony.com/schema/dic/services/services-1.0.xsd
8+
http://symfony.com/schema/dic/security
9+
https://symfony.com/schema/dic/security/security-1.0.xsd">
10+
11+
<config>
12+
<provider name="default" id="foo" />
13+
14+
<firewall name="main" provider="default">
15+
<form-login />
16+
<logout>
17+
<clear-site-data>cookies</clear-site-data>
18+
<clear-site-data>executionContexts</clear-site-data>
19+
</logout>
20+
</firewall>
21+
</config>
22+
</srv:container>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
security:
2+
providers:
3+
default:
4+
id: foo
5+
6+
firewalls:
7+
main:
8+
provider: default
9+
form_login: true
10+
logout:
11+
clear_site_data:
12+
- cookies
13+
- executionContexts

Tests/DependencyInjection/SecurityExtensionTest.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,48 @@ public function testConfigureCustomFirewallListener()
848848
$this->assertContains('custom_firewall_listener_id', $firewallListeners);
849849
}
850850

851+
public function testClearSiteDataLogoutListenerEnabled()
852+
{
853+
$container = $this->getRawContainer();
854+
855+
$firewallId = 'logout_firewall';
856+
$container->loadFromExtension('security', [
857+
'firewalls' => [
858+
$firewallId => [
859+
'logout' => [
860+
'clear_site_data' => ['*'],
861+
],
862+
],
863+
],
864+
]);
865+
866+
$container->compile();
867+
868+
$this->assertTrue($container->has('security.logout.listener.clear_site_data.'.$firewallId));
869+
$listenerArgument = $container->getDefinition('security.logout.listener.clear_site_data.'.$firewallId)->getArgument(0);
870+
$this->assertSame(['*'], $listenerArgument);
871+
}
872+
873+
public function testClearSiteDataLogoutListenerDisabled()
874+
{
875+
$container = $this->getRawContainer();
876+
877+
$firewallId = 'logout_firewall';
878+
$container->loadFromExtension('security', [
879+
'firewalls' => [
880+
$firewallId => [
881+
'logout' => [
882+
'clear_site_data' => [],
883+
],
884+
],
885+
],
886+
]);
887+
888+
$container->compile();
889+
890+
$this->assertFalse($container->has('security.logout.listener.clear_site_data.'.$firewallId));
891+
}
892+
851893
/**
852894
* @group legacy
853895
*/

0 commit comments

Comments
 (0)