8000 [Validator] Improve entropy estimation in PasswordStrengthValidator · umpirsky/symfony@99c09ff · GitHub
[go: up one dir, main page]

Skip to content

Commit 99c09ff

Browse files
[Validator] Improve entropy estimation in PasswordStrengthValidator
1 parent 48d5236 commit 99c09ff

File tree

3 files changed

+41
-8
lines changed

3 files changed

+41
-8
lines changed

src/Symfony/Component/Validator/Constraints/PasswordStrength.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,16 @@ final class PasswordStrength extends Constraint
4040

4141
public int $minScore;
4242

43-
public function __construct(int $minScore = self::STRENGTH_REASONABLE, mixed $options = null, array $groups = null, mixed $payload = null)
43+
public function __construct(array $options = null, int $minScore = null, array $groups = null, mixed $payload = null)
4444
{
45-
if (isset($minScore) && (!\is_int($minScore) || $minScore < 1 || $minScore > 4)) {
46-
throw new ConstraintDefinitionException(sprintf('The parameter "minScore" of the "%s" constraint must be an integer between 1 and 4.', static::class));
47-
}
48-
$options['minScore'] = $minScore;
45+
$options['minScore'] ??= self::STRENGTH_REASONABLE;
46+
4947
parent::__construct($options, $groups, $payload);
48+
49+
$this->minScore = $minScore ?? $this->minScore;
50+
51+
if ($this->minScore < 1 || 4 < $this->minScore) {
52+
throw new ConstraintDefinitionException(sprintf('The parameter "minScore" of the "%s" constraint must be an integer between 1 and 4.', self::class));
53+
}
5054
}
5155
}

src/Symfony/Component/Validator/Constraints/PasswordStrengthValidator.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,26 @@ public function validate(#[\SensitiveParameter] mixed $value, Constraint $constr
5858
*/
5959
private static function estimateStrength(#[\SensitiveParameter] string $password): int
6060
{
61-
$entropy = log(\strlen(count_chars($password, 3)) ** \strlen($password), 2);
61+
if (!$length = \strlen($password)) {
62+
return PasswordStrength::STRENGTH_VERY_WEAK;
63+
}
64+
$password = count_chars($password, 1);
65+
$chars = \count($password);
66+
67+
$control = $digit = $upper = $lower = $symbol = $other = 0;
68+
foreach ($password as $chr => $count) {
69+
match (true) {
70+
$chr < 32 || 127 === $chr => $control = 33,
71+
48 <= $chr && $chr <= 57 => $digit = 10,
72+
65 <= $chr && $chr <= 90 => $upper = 26,
73+
97 <= $chr && $chr <= 122 => $lower = 26,
74+
128 <= $chr => $other = 128,
75+
default => $symbol = 33,
76+
};
77+
}
78+
79+
$pool = $lower + $upper + $digit + $symbol + $control + $other;
80+
$entropy = $chars * log($pool, 2) + ($length - $chars) * log($chars, 2);
6281

6382
return match (true) {
6483
$entropy >= 120 => PasswordStrength::STRENGTH_VERY_STRONG,

src/Symfony/Component/Validator/Tests/Constraints/PasswordStrengthValidatorTest.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,22 @@ public function testValidValues(string $value, int $expectedStrength)
3030
$this->validator->validate($value, new PasswordStrength(minScore: $expectedStrength));
3131

3232
$this->assertNoViolation();
33+
34+
if (PasswordStrength::STRENGTH_VERY_STRONG === $expectedStrength) {
35+
return;
36+
}
37+
38+
$this->validator->validate($value, new PasswordStrength(minScore: $expectedStrength + 1));
39+
40+
$this->buildViolation('The password strength is too low. Please use a stronger password.')
41+
->setCode(PasswordStrength::PASSWORD_STRENGTH_ERROR)
42+
->assertRaised();
3343
}
3444

3545
public static function getValidValues(): iterable
3646
{
37-
yield ['How-is this 🤔?!', PasswordStrength::STRENGTH_WEAK];
38-
yield ['Reasonable-pwd-❤️', PasswordStrength::STRENGTH_REASONABLE];
47+
yield ['How-is-this', PasswordStrength::STRENGTH_WEAK];
48+
yield ['Reasonable-pwd', PasswordStrength::STRENGTH_REASONABLE];
3949
yield ['This 1s a very g00d Pa55word! ;-)', PasswordStrength::STRENGTH_VERY_STRONG];
4050
yield ['pudding-smack-👌🏼-fox-😎', PasswordStrength::STRENGTH_VERY_STRONG];
4151
}

0 commit comments

Comments
 (0)
0