Skip to content

Commit 5be0da2

Browse files
committed
Hiding userFqcn in RememberMe cookie
1 parent e348b70 commit 5be0da2

File tree

7 files changed

+28
-23
lines changed

7 files changed

+28
-23
lines changed

src/Symfony/Component/Security/Http/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ CHANGELOG
33

44
7.3
55
---
6-
6+
* Rename property userFqcn to userFqcnHash, remove method getUserFqcn, add method getUserFqcnHash.
77
* Add encryption support to `OidcTokenHandler` (JWE)
88

99
7.2

src/Symfony/Component/Security/Http/RememberMe/PersistentRememberMeHandler.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public function consumeRememberMeCookie(RememberMeDetails $rememberMeDetails): U
6767
[$series, $tokenValue] = explode(':', $rememberMeDetails->getValue(), 2);
6868
$persistentToken = $this->tokenProvider->loadTokenBySeries($series);
6969

70-
if ($persistentToken->getUserIdentifier() !== $rememberMeDetails->getUserIdentifier() || $persistentToken->getClass() !== $rememberMeDetails->getUserFqcn()) {
70+
if ($persistentToken->getUserIdentifier() !== $rememberMeDetails->getUserIdentifier() || !hash_equals(RememberMeDetails::computeUserFqcnHash($persistentToken->getClass()), $rememberMeDetails->getUserFqcnHash())) {
7171
throw new AuthenticationException('The cookie\'s hash is invalid.');
7272
}
7373

@@ -89,7 +89,7 @@ public function consumeRememberMeCookie(RememberMeDetails $rememberMeDetails): U
8989
}
9090

9191
return parent::consumeRememberMeCookie(new RememberMeDetails(
92-
$persistentToken->getClass(),
92+
RememberMeDetails::computeUserFqcnHash($persistentToken->getClass()),
9393
$persistentToken->getUserIdentifier(),
9494
$expires,
9595
$persistentToken->getLastUsed()->getTimestamp().':'.$series.':'.$tokenValue.':'.$persistentToken->getClass()

src/Symfony/Component/Security/Http/RememberMe/RememberMeDetails.php

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class RememberMeDetails
2222
public const COOKIE_DELIMITER = ':';
2323

2424
public function __construct(
25-
private string $userFqcn,
25+
private string $userFqcnHash,
2626
private string $userIdentifier,
2727
private int $expires,
2828
private string $value,
@@ -48,7 +48,12 @@ public static function fromRawCookie(string $rawCookie): self
4848

4949
public static function fromPersistentToken(PersistentToken $persistentToken, int $expires): self
5050
{
51-
return new static($persistentToken->getClass(), $persistentToken->getUserIdentifier(), $expires, $persistentToken->getSeries().':'.$persistentToken->getTokenValue());
51+
return new static(self::computeUserFqcnHash($persistentToken->getClass()), $persistentToken->getUserIdentifier(), $expires, $persistentToken->getSeries().':'.$persistentToken->getTokenValue());
52+
}
53+
54+
public static function computeUserFqcnHash(string $userFqcn): string
55+
{
56+
return hash('sha256', $userFqcn);
5257
}
5358

5459
public function withValue(string $value): self
@@ -59,9 +64,9 @@ public function withValue(string $value): self
5964
return $details;
6065
}
6166

62-
public function getUserFqcn(): string
67+
public function getUserFqcnHash(): string
6368
{
64-
return $this->userFqcn;
69+
return $this->userFqcnHash;
6570
}
6671

6772
public function getUserIdentifier(): string
@@ -82,6 +87,6 @@ public function getValue(): string
8287
public function toString(): string
8388
{
8489
// $userIdentifier is encoded because it might contain COOKIE_DELIMITER, we assume other values don't
85-
return implode(self::COOKIE_DELIMITER, [strtr($this->userFqcn, '\\', '.'), strtr(base64_encode($this->userIdentifier), '+/=', '-_~'), $this->expires, $this->value]);
90+
return implode(self::COOKIE_DELIMITER, [$this->userFqcnHash, strtr(base64_encode($this->userIdentifier), '+/=', '-_~'), $this->expires, $this->value]);
8691
}
8792
}

src/Symfony/Component/Security/Http/RememberMe/SignatureRememberMeHandler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public function createRememberMeCookie(UserInterface $user): void
4747
$expires = time() + $this->options['lifetime'];
4848
$value = $this->signatureHasher->computeSignatureHash($user, $expires);
4949

50-
$details = new RememberMeDetails($user::class, $user->getUserIdentifier(), $expires, $value);
50+
$details = new RememberMeDetails(RememberMeDetails::computeUserFqcnHash($user::class), $user->getUserIdentifier(), $expires, $value);
5151
$this->createCookie($details);
5252
}
5353

src/Symfony/Component/Security/Http/Tests/Authenticator/RememberMeAuthenticatorTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public static function provideSupportsData()
6969

7070
public function testAuthenticate()
7171
{
72-
$rememberMeDetails = new RememberMeDetails(InMemoryUser::class, 'wouter', 1, 'secret');
72+
$rememberMeDetails = new RememberMeDetails(RememberMeDetails::computeUserFqcnHash(InMemoryUser::class), 'wouter', 1, 'secret');
7373
$request = Request::create('/', 'GET', [], ['_remember_me_cookie' => $rememberMeDetails->toString()]);
7474
$passport = $this->authenticator->authenticate($request);
7575

src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentRememberMeHandlerTest.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public function testClearRememberMeCookie()
6363
->method('deleteTokenBySeries')
6464
->with('series1');
6565

66-
$this->request->cookies->set('REMEMBERME', (new RememberMeDetails(InMemoryUser::class, 'wouter', 0, 'series1:tokenvalue'))->toString());
66+
$this->request->cookies->set('REMEMBERME', (new RememberMeDetails(RememberMeDetails::computeUserFqcnHash(InMemoryUser::class), 'wouter', 0, 'series1:tokenvalue'))->toString());
6767

6868
$this->handler->clearRememberMeCookie();
6969

@@ -84,7 +84,7 @@ public function testConsumeRememberMeCookieValid()
8484

8585
$this->tokenProvider->expects($this->once())->method('updateToken')->with('series1');
8686

87-
$rememberMeDetails = new RememberMeDetails(InMemoryUser::class, 'wouter', 360, 'series1:tokenvalue');
87+
$rememberMeDetails = new RememberMeDetails(RememberMeDetails::computeUserFqcnHash(InMemoryUser::class), 'wouter', 360, 'series1:tokenvalue');
8888
$this->handler->consumeRememberMeCookie($rememberMeDetails);
8989

9090
// assert that the cookie has been updated with a new base64 encoded token value
@@ -110,7 +110,7 @@ public function testConsumeRememberMeCookieInvalidOwner()
110110
->willReturn(new PersistentToken(InMemoryUser::class, 'wouter', 'series1', 'tokenvalue', new \DateTime('-10 min')))
111111
;
112112

113-
$rememberMeDetails = new RememberMeDetails(InMemoryUser::class, 'jeremy', 360, 'series1:tokenvalue');
113+
$rememberMeDetails = new RememberMeDetails(RememberMeDetails::computeUserFqcnHash(InMemoryUser::class), 'jeremy', 360, 'series1:tokenvalue');
114114

115115
$this->expectException(AuthenticationException::class);
116116
$this->expectExceptionMessage('The cookie\'s hash is invalid.');
@@ -125,7 +125,7 @@ public function testConsumeRememberMeCookieInvalidValue()
125125
->willReturn(new PersistentToken(InMemoryUser::class, 'wouter', 'series1', 'tokenvalue', new \DateTime('-10 min')))
126126
;
127127

128-
$rememberMeDetails = new RememberMeDetails(InMemoryUser::class, 'wouter', 360, 'series1:tokenvalue:somethingelse');
128+
$rememberMeDetails = new RememberMeDetails(RememberMeDetails::computeUserFqcnHash(InMemoryUser::class), 'wouter', 360, 'series1:tokenvalue:somethingelse');
129129

130130
$this->expectException(AuthenticationException::class);
131131
$this->expectExceptionMessage('This token was already used. The account is possibly compromised.');
@@ -151,7 +151,7 @@ public function testConsumeRememberMeCookieValidByValidatorWithoutUpdate()
151151
->willReturn(true)
152152
;
153153

154-
$rememberMeDetails = new RememberMeDetails(InMemoryUser::class, 'wouter', 360, 'series1:oldTokenValue');
154+
$rememberMeDetails = new RememberMeDetails(RememberMeDetails::computeUserFqcnHash(InMemoryUser::class), 'wouter', 360, 'series1:oldTokenValue');
155155
$handler->consumeRememberMeCookie($rememberMeDetails);
156156

157157
$this->assertFalse($this->request->attributes->has(ResponseListener::COOKIE_ATTR_NAME));
@@ -168,7 +168,7 @@ public function testConsumeRememberMeCookieInvalidToken()
168168

169169
$this->expectException(CookieTheftException::class);
170170

171-
$this->handler->consumeRememberMeCookie(new RememberMeDetails(InMemoryUser::class, 'wouter', 360, 'series1:tokenvalue'));
171+
$this->handler->consumeRememberMeCookie(new RememberMeDetails(RememberMeDetails::computeUserFqcnHash(InMemoryUser::class), 'wouter', 360, 'series1:tokenvalue'));
172172
}
173173

174174
public function testConsumeRememberMeCookieExpired()
@@ -183,7 +183,7 @@ public function testConsumeRememberMeCookieExpired()
183183
$this->expectException(AuthenticationException::class);
184184
$this->expectExceptionMessage('The cookie has expired.');
185185

186-
$this->handler->consumeRememberMeCookie(new RememberMeDetails(InMemoryUser::class, 'wouter', 360, 'series1:tokenvalue'));
186+
$this->handler->consumeRememberMeCookie(new RememberMeDetails(RememberMeDetails::computeUserFqcnHash(InMemoryUser::class), 'wouter', 360, 'series1:tokenvalue'));
187187
}
188188

189189
public function testBase64EncodedTokens()
@@ -196,7 +196,7 @@ public function testBase64EncodedTokens()
196196

197197
$this->tokenProvider->expects($this->once())->method('updateToken')->with('series1');
198198

199-
$rememberMeDetails = new RememberMeDetails(InMemoryUser::class, 'wouter', 360, 'series1:tokenvalue');
199+
$rememberMeDetails = new RememberMeDetails(RememberMeDetails::computeUserFqcnHash(InMemoryUser::class), 'wouter', 360, 'series1:tokenvalue');
200200
$rememberMeDetails = RememberMeDetails::fromRawCookie(base64_encode($rememberMeDetails->toString()));
201201
$this->handler->consumeRememberMeCookie($rememberMeDetails);
202202
}

src/Symfony/Component/Security/Http/Tests/RememberMe/SignatureRememberMeHandlerTest.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public function testCreateRememberMeCookie()
5757

5858
/** @var Cookie $cookie */
5959
$cookie = $this->request->attributes->get(ResponseListener::COOKIE_ATTR_NAME);
60-
$this->assertEquals(strtr(InMemoryUser::class, '\\', '.').':d291dGVy:'.$expire.':'.$signature, $cookie->getValue());
60+
$this->assertEquals(RememberMeDetails::computeUserFqcnHash(InMemoryUser::class).':d291dGVy:'.$expire.':'.$signature, $cookie->getValue());
6161
}
6262

6363
public function testClearRememberMeCookie()
@@ -77,21 +77,21 @@ public function testConsumeRememberMeCookieValid()
7777
$signature = $this->signatureHasher->computeSignatureHash($user, $expire = time() + 3600);
7878
$this->userProvider->createUser(new InMemoryUser('wouter', null));
7979

80-
$rememberMeDetails = new RememberMeDetails(InMemoryUser::class, 'wouter', $expire, $signature);
80+
$rememberMeDetails = new RememberMeDetails(RememberMeDetails::computeUserFqcnHash(InMemoryUser::class), 'wouter', $expire, $signature);
8181
$this->handler->consumeRememberMeCookie($rememberMeDetails);
8282

8383
$this->assertTrue($this->request->attributes->has(ResponseListener::COOKIE_ATTR_NAME));
8484

8585
/** @var Cookie $cookie */
8686
$cookie = $this->request->attributes->get(ResponseListener::COOKIE_ATTR_NAME);
87-
$this->assertNotEquals((new RememberMeDetails(InMemoryUser::class, 'wouter', $expire, $signature))->toString(), $cookie->getValue());
87+
$this->assertNotEquals((new RememberMeDetails(RememberMeDetails::computeUserFqcnHash(InMemoryUser::class), 'wouter', $expire, $signature))->toString(), $cookie->getValue());
8888
}
8989

9090
public function testConsumeRememberMeCookieInvalidHash()
9191
{
9292
$this->expectException(AuthenticationException::class);
9393
$this->expectExceptionMessage('The cookie\'s hash is invalid.');
94-
$this->handler->consumeRememberMeCookie(new RememberMeDetails(InMemoryUser::class, 'wouter', time() + 600, 'badsignature'));
94+
$this->handler->consumeRememberMeCookie(new RememberMeDetails(RememberMeDetails::computeUserFqcnHash(InMemoryUser::class), 'wouter', time() + 600, 'badsignature'));
9595
}
9696

9797
public function testConsumeRememberMeCookieExpired()
@@ -101,6 +101,6 @@ public function testConsumeRememberMeCookieExpired()
101101

102102
$this->expectException(AuthenticationException::class);
103103
$this->expectExceptionMessage('The cookie has expired.');
104-
$this->handler->consumeRememberMeCookie(new RememberMeDetails(InMemoryUser::class, 'wouter', 360, $signature));
104+
$this->handler->consumeRememberMeCookie(new RememberMeDetails(RememberMeDetails::computeUserFqcnHash(InMemoryUser::class), 'wouter', 360, $signature));
105105
}
106106
}

0 commit comments

Comments
 (0)