From b501bba60594b9286bafcc9fdb11f69b3ec6cad6 Mon Sep 17 00:00:00 2001 From: Stephan Vock Date: Mon, 26 Feb 2024 10:53:07 +0000 Subject: [PATCH] [Security] Validate that CSRF token in form login is string similar to username/password --- .../Authenticator/FormLoginAuthenticator.php | 4 ++ .../FormLoginAuthenticatorTest.php | 48 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/Symfony/Component/Security/Http/Authenticator/FormLoginAuthenticator.php b/src/Symfony/Component/Security/Http/Authenticator/FormLoginAuthenticator.php index 3279067f50f7a..5b4de2b454d69 100644 --- a/src/Symfony/Component/Security/Http/Authenticator/FormLoginAuthenticator.php +++ b/src/Symfony/Component/Security/Http/Authenticator/FormLoginAuthenticator.php @@ -161,6 +161,10 @@ private function getCredentials(Request $request): array throw new BadRequestHttpException(sprintf('The key "%s" must be a string, "%s" given.', $this->options['password_parameter'], \gettype($credentials['password']))); } + if (!\is_string($credentials['csrf_token'] ?? '') && (!\is_object($credentials['csrf_token']) || !method_exists($credentials['csrf_token'], '__toString'))) { + throw new BadRequestHttpException(sprintf('The key "%s" must be a string, "%s" given.', $this->options['csrf_parameter'], \gettype($credentials['csrf_token']))); + } + return $credentials; } diff --git a/src/Symfony/Component/Security/Http/Tests/Authenticator/FormLoginAuthenticatorTest.php b/src/Symfony/Component/Security/Http/Tests/Authenticator/FormLoginAuthenticatorTest.php index ca0dd119b89ef..83b2bdb03243e 100644 --- a/src/Symfony/Component/Security/Http/Tests/Authenticator/FormLoginAuthenticatorTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Authenticator/FormLoginAuthenticatorTest.php @@ -165,6 +165,54 @@ public function __toString() $this->assertSame('s$cr$t', $credentialsBadge->getPassword()); } + /** + * @dataProvider postOnlyDataProvider + */ + public function testHandleNonStringCsrfTokenWithArray($postOnly) + { + $request = Request::create('/login_check', 'POST', ['_username' => 'foo', 'password' => 'bar', '_csrf_token' => []]); + $request->setSession($this->createSession()); + + $this->setUpAuthenticator(['post_only' => $postOnly]); + + $this->expectException(BadRequestHttpException::class); + $this->expectExceptionMessage('The key "_csrf_token" must be a string, "array" given.'); + + $this->authenticator->authenticate($request); + } + + /** + * @dataProvider postOnlyDataProvider + */ + public function testHandleNonStringCsrfTokenWithInt($postOnly) + { + $request = Request::create('/login_check', 'POST', ['_username' => 'foo', 'password' => 'bar', '_csrf_token' => 42]); + $request->setSession($this->createSession()); + + $this->setUpAuthenticator(['post_only' => $postOnly]); + + $this->expectException(BadRequestHttpException::class); + $this->expectExceptionMessage('The key "_csrf_token" must be a string, "integer" given.'); + + $this->authenticator->authenticate($request); + } + + /** + * @dataProvider postOnlyDataProvider + */ + public function testHandleNonStringCsrfTokenWithObject($postOnly) + { + $request = Request::create('/login_check', 'POST', ['_username' => 'foo', 'password' => 'bar', '_csrf_token' => new \stdClass()]); + $request->setSession($this->createSession()); + + $this->setUpAuthenticator(['post_only' => $postOnly]); + + $this->expectException(BadRequestHttpException::class); + $this->expectExceptionMessage('The key "_csrf_token" must be a string, "object" given.'); + + $this->authenticator->authenticate($request); + } + public static function postOnlyDataProvider() { yield [true];