Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Symfony/Component/Security/Csrf/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
CHANGELOG
=========

7.4
---

* Add support for `Sec-Fetch-Site` to `SameOriginCsrfTokenManager`

7.2
---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,24 @@
*
* This manager is designed to be stateless and compatible with HTTP-caching.
*
* First, we validate the source of the request using the Origin/Referer headers. This relies
* on the app being able to know its own target origin. Don't miss configuring your reverse proxy to
* send the X-Forwarded-* / Forwarded headers if you're behind one.
* Requests are considered secure when either:
* - the Sec-Fetch-Site header contains 'same-origin';
* - the Origin or Referer headers contain the same origin as the request;
* - a special token was double-submitted in the request payload and as a cookie and/or a header.
*
* Then, we validate the request using a cookie and a CsrfToken. If the cookie is found, it should
* contain the same value as the CsrfToken. A JavaScript snippet on the client side is responsible
* for performing this double-submission. The token value should be regenerated on every request
* using a cryptographically secure random generator.
* The check using the Origin/Referer headers relies on the app being able to know its own target
* origin. Don't miss configuring your reverse proxy to send the X-Forwarded-* / Forwarded headers
* if you're behind one.
*
* If either double-submit or Origin/Referer headers are missing, it typically indicates that
* JavaScript is disabled on the client side, or that the JavaScript snippet was not properly
* implemented, or that the Origin/Referer headers were filtered out.
*
* Requests lacking both double-submit and origin information are deemed insecure.
* The check relying on the double-submit requires a JavaScript snippet on the client side,
* responsible for generating a cryptographically-secure random token and attaching it to the request
* payload and as a cookie and/or a header. This check is meant to cover the case where neither
* Sec-Fetch-Site, nor Origin/Referer headers are present.
*
* When a session is found, a behavioral check is added to ensure that the validation method does not
* downgrade from double-submit to origin checks. This prevents attackers from exploiting potentially
* less secure validation methods once a more secure method has been confirmed as functional.
* downgrade from double-submit to origin checks, and vice versa. This prevents attackers from
* exploiting potentially less secure validation methods once a more secure method has been confirmed
* as functional.
*
* On HTTPS connections, the cookie is prefixed with "__Host-" to prevent it from being forged on an
* HTTP channel. On the JS side, the cookie should be set with samesite=strict to strengthen the CSRF
Expand All @@ -50,8 +50,8 @@
* cookie. This makes it harder for an attacker to forge a request, though it may also pose challenges
* when setting the header depending on the client-side framework in use.
*
* When a fallback CSRF token manager is provided, only tokens listed in the $tokenIds argument will be
* managed by this manager. All other tokens will be delegated to the fallback manager.
* When a fallback CSRF token manager is provided, only tokens listed in the $tokenIds argument will
* be managed by this manager. All other tokens will be delegated to the fallback manager.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
Expand Down Expand Up @@ -235,6 +235,10 @@ public function onKernelResponse(ResponseEvent $event): void
*/
private function isValidOrigin(Request $request): ?bool
{
if (null !== $header = $request->headers->get('Sec-Fetch-Site')) {
return 'same-origin' === $header;
}

$target = $request->getSchemeAndHttpHost().'/';
$source = 'null';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,31 @@ public function testValidRefererInvalidOrigin()
$this->assertSame(1 << 8, $request->attributes->get('csrf-token'));
}

public function testSecFetchSiteSameOrigin()
{
$request = new Request();
$request->headers->set('Sec-Fetch-Site', 'same-origin');
$this->requestStack->push($request);

$token = new CsrfToken('test_token', str_repeat('a', 24));

$this->logger->expects($this->once())->method('debug')->with('CSRF validation accepted using origin info.');
$this->assertTrue($this->csrfTokenManager->isTokenValid($token));
$this->assertSame(1 << 8, $request->attributes->get('csrf-token'));
}

public function testSecFetchSiteCrossSite()
{
$request = new Request();
$request->headers->set('Sec-Fetch-Site', 'cross-site');
$this->requestStack->push($request);

$token = new CsrfToken('test_token', str_repeat('a', 24));

$this->logger->expects($this->once())->method('warning')->with('CSRF validation failed: origin info doesn\'t match.');
$this->assertFalse($this->csrfTokenManager->isTokenValid($token));
}

public function testValidOriginAfterDoubleSubmit()
{
$session = $this->createMock(Session::class);
Expand Down
Loading