Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 9cab9e1

Browse filesBrowse files
[Security] Support hashing the hashed password using crc32c when putting the user in the session
1 parent 0134078 commit 9cab9e1
Copy full SHA for 9cab9e1

File tree

Expand file treeCollapse file tree

4 files changed

+44
-8
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+44
-8
lines changed

‎src/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.php
+18Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,24 @@
1414
/**
1515
* For users that can be authenticated using a password.
1616
*
17+
* The __serialize/__unserialize() magic methods can be used on the user class to prevent the password hash from being
18+
* stored in the session. If the password is not stored at all in the session, getPassword() should return null after
19+
* unserialization, and then, changing the user's password won't invalidate its sessions.
20+
* In order to invalidate the user sessions while not storing the password hash in the session, it's also possible to
21+
* hash the password hash before serializing it; crc32c is the only algorithm supported. For example:
22+
*
23+
* public function __serialize(): array
24+
* {
25+
* return [$this->username, hash('crc32c', $this->password)];
26+
* }
27+
*
28+
* public function __unserialize(array $data): void
29+
* {
30+
* [$this->username, $this->password] = $data;
31+
* }
32+
*
33+
* Implement EquatableInteface if you need another logic.
34+
*
1735
* @author Robin Chalas <robin.chalas@gmail.com>
1836
* @author Wouter de Jong <wouter@wouterj.nl>
1937
*/

‎src/Symfony/Component/Security/Http/Firewall/ContextListener.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Http/Firewall/ContextListener.php
+8-4Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -292,9 +292,13 @@ private static function hasUserChanged(UserInterface $originalUser, TokenInterfa
292292
}
293293

294294
if ($originalUser instanceof PasswordAuthenticatedUserInterface || $refreshedUser instanceof PasswordAuthenticatedUserInterface) {
295-
if (!$originalUser instanceof PasswordAuthenticatedUserInterface
296-
|| !$refreshedUser instanceof PasswordAuthenticatedUserInterface
297-
|| $refreshedUser->getPassword() !== ($originalUser->getPassword() ?? $refreshedUser->getPassword())
295+
if (!$originalUser instanceof PasswordAuthenticatedUserInterface || !$refreshedUser instanceof PasswordAuthenticatedUserInterface) {
296+
return true;
297+
}
298+
299+
if (null !== ($originalPassword = $originalUser->getPassword())
300+
&& ($refreshedPassword = $refreshedUser->getPassword()) !== $originalPassword
301+
&& (8 !== \strlen($originalPassword) || hash('crc32c', $refreshedPassword ?? $originalPassword) !== $originalPassword)
298302
) {
299303
return true;
300304
}
@@ -303,7 +307,7 @@ private static function hasUserChanged(UserInterface $originalUser, TokenInterfa
303307
return true;
304308
}
305309

306-
if ($originalUser instanceof LegacyPasswordAuthenticatedUserInterface && $refreshedUser instanceof LegacyPasswordAuthenticatedUserInterface && $originalUser->getSalt() !== $refreshedUser->getSalt()) {
310+
if ($originalUser instanceof LegacyPasswordAuthenticatedUserInterface && $originalUser->getSalt() !== $refreshedUser->getSalt()) {
307311
return true;
308312
}
309313
}

‎src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php
+6-2Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,9 +377,13 @@ public function testOnKernelResponseRemoveListener()
377377
$this->assertEmpty($dispatcher->getListeners());
378378
}
379379

380-
public function testRemovingPasswordFromSessionDoesntInvalidateTheToken()
380+
/**
381+
* @testWith [true]
382+
* [false]
383+
*/
384+
public function testNullOrHashedPasswordInSessionDoesntInvalidateTheToken(bool $hashPassword)
381385
{
382-
$user = new CustomUser('user', ['ROLE_USER'], 'pass');
386+
$user = new CustomUser('user', ['ROLE_USER'], 'pass', $hashPassword);
383387

384388
$userProvider = $this->createMock(UserProviderInterface::class);
385389
$userProvider->expects($this->once())

‎src/Symfony/Component/Security/Http/Tests/Fixtures/CustomUser.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Http/Tests/Fixtures/CustomUser.php
+12-2Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ final class CustomUser implements UserInterface, PasswordAuthenticatedUserInterf
1919
public function __construct(
2020
private string $username,
2121
private array $roles,
22-
private ?string $password = null,
22+
private ?string $password,
23+
private bool $hashPassword,
2324
) {
2425
}
2526

@@ -44,6 +45,15 @@ public function eraseCredentials(): void
4445

4546
public function __serialize(): array
4647
{
47-
return [\sprintf("\0%s\0username", self::class) => $this->username];
48+
$data = (array) $this;
49+
$passwordKey = \sprintf("\0%s\0password", self::class);
50+
51+
if ($this->hashPassword) {
52+
$data[$passwordKey] = hash('crc32c', $this->password);
53+
} else {
54+
unset($data[$passwordKey]);
55+
}
56+
57+
return $data;
4858
}
4959
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.