From 64b65f2e35d5443a750ac7729652f4b6676a941b Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Sun, 23 Nov 2025 03:03:34 +0100 Subject: [PATCH] [Security] Fix UserBadge validation bypass via identifier normalizer --- Authenticator/Passport/Badge/UserBadge.php | 22 ++++++++++++------ .../Passport/Badge/UserBadgeTest.php | 23 +++++++++++++++++++ 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/Authenticator/Passport/Badge/UserBadge.php b/Authenticator/Passport/Badge/UserBadge.php index d936ff62..874d770d 100644 --- a/Authenticator/Passport/Badge/UserBadge.php +++ b/Authenticator/Passport/Badge/UserBadge.php @@ -54,14 +54,8 @@ public function __construct( private ?array $attributes = null, ?\Closure $identifierNormalizer = null, ) { - if ('' === $userIdentifier) { - trigger_deprecation('symfony/security-http', '7.2', 'Using an empty string as user identifier is deprecated and will throw an exception in Symfony 8.0.'); - // throw new BadCredentialsException('Empty user identifier.'); - } + $this->validateUserIdentifier($userIdentifier); - if (\strlen($userIdentifier) > self::MAX_USERNAME_LENGTH) { - throw new BadCredentialsException('Username too long.'); - } if ($identifierNormalizer) { $this->identifierNormalizer = static fn () => $identifierNormalizer($userIdentifier); } @@ -74,6 +68,8 @@ public function getUserIdentifier(): string if (isset($this->identifierNormalizer)) { $this->userIdentifier = ($this->identifierNormalizer)(); $this->identifierNormalizer = null; + + $this->validateUserIdentifier($this->userIdentifier); } return $this->userIdentifier; @@ -132,4 +128,16 @@ public function isResolved(): bool { return true; } + + private function validateUserIdentifier(string $userIdentifier): void + { + if ('' === $userIdentifier) { + trigger_deprecation('symfony/security-http', '7.2', 'Using an empty string as user identifier is deprecated and will throw an exception in Symfony 8.0.'); + // throw new BadCredentialsException('Empty user identifier.'); + } + + if (\strlen($userIdentifier) > self::MAX_USERNAME_LENGTH) { + throw new BadCredentialsException('Username too long.'); + } + } } diff --git a/Tests/Authenticator/Passport/Badge/UserBadgeTest.php b/Tests/Authenticator/Passport/Badge/UserBadgeTest.php index f648d048..7e10a8d3 100644 --- a/Tests/Authenticator/Passport/Badge/UserBadgeTest.php +++ b/Tests/Authenticator/Passport/Badge/UserBadgeTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; +use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\String\Slugger\AsciiSlugger; @@ -69,4 +70,26 @@ public static function provideUserIdentifierNormalizationData(): iterable yield 'Greek to ASCII' => ['ΝιΚόΛΑος', 'NIKOLAOS', $upperAndAscii]; yield 'Katakana to ASCII' => ['たなかそういち', 'TANAKASOUICHI', $upperAndAscii]; } + + /** + * @group legacy + */ + public function testUserIdentifierNormalizationTriggersDeprecationForEmptyString() + { + $badge = new UserBadge('valid_input', null, null, fn () => ''); + + $this->expectUserDeprecationMessage('Since symfony/security-http 7.2: Using an empty string as user identifier is deprecated and will throw an exception in Symfony 8.0.'); + + $this->assertSame('', $badge->getUserIdentifier()); + } + + public function testUserIdentifierNormalizationEnforcesMaxLength() + { + $badge = new UserBadge('valid_input', null, null, fn () => str_repeat('a', UserBadge::MAX_USERNAME_LENGTH + 1)); + + $this->expectException(BadCredentialsException::class); + $this->expectExceptionMessage('Username too long.'); + + $badge->getUserIdentifier(); + } }