Add a "have i been pwned" validator
This commit is contained in:
7 files changed
+101
-1
No files matched your search
@@ -54,14 +54,17 @@ terminal42_password_validation:
|
||||
other_chars: "+*ç%&/()=?"
|
||||
password_history: 10
|
||||
change_days: 90
|
||||
haveibeenpwned: 1
|
||||
Contao\BackendUser:
|
||||
min_length: 10
|
||||
haveibeenpwned: 1
|
||||
```
|
||||
|
||||
Parameter | Purpose
|
||||
--------- | -------
|
||||
`password_history`: | Keep track of the latest `n` passwords, and force the users not to choose one of their recent passwords.
|
||||
`change_days`: | Ask the user to change their password after certain days.
|
||||
`haveibeenpwned`: | Check the user password against known data breaches reported to [';--have i been pwned?](https://haveibeenpwned.com/). The configuration allows you to specify an integer to define the [minimum number of data breaches](https://haveibeenpwned.com/API/v3#SearchingPwnedPasswordsByRange) the password needs to occur in to fail password validation.
|
||||
|
||||
### Password-change
|
||||
|
||||
|
||||
+3
-1
@@ -27,10 +27,12 @@
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"contao/core-bundle": "^4.9",
|
||||
"guzzlehttp/guzzle": "^6.2",
|
||||
"paragonie/hidden-string": "^1.0",
|
||||
"symfony/config": "^4.0 || ^5.0",
|
||||
"symfony/dependency-injection": "^4.0 || ^5.0",
|
||||
"symfony/http-kernel": "^4.0 || ^5.0"
|
||||
"symfony/http-kernel": "^4.0 || ^5.0",
|
||||
"terminal42/notification_center": "^1.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"contao/manager-plugin": "^2.7",
|
||||
|
||||
@@ -43,6 +43,7 @@ final class Configuration implements ConfigurationInterface
|
||||
->end()
|
||||
->integerNode('password_history')->end()
|
||||
->integerNode('change_days')->end()
|
||||
->integerNode('haveibeenpwned')->end()
|
||||
->scalarNode('other_chars')->end()
|
||||
->arrayNode('require')
|
||||
->children()
|
||||
|
||||
@@ -26,3 +26,10 @@ services:
|
||||
- { name: terminal42_password_validation.validator, alias: required_characters }
|
||||
arguments:
|
||||
['@terminal42_password_validation.validator_configuration']
|
||||
|
||||
terminal42_password_validation.validator.haveibeenpwned:
|
||||
class: Terminal42\PasswordValidationBundle\Validation\Validator\HaveIBeenPwned
|
||||
tags:
|
||||
- { name: terminal42_password_validation.validator, alias: haveibeenpwned }
|
||||
arguments:
|
||||
['@terminal42_password_validation.validator_configuration']
|
||||
@@ -35,6 +35,10 @@
|
||||
<source>Please use at least %u uppercase letters, %u lowercase letters, %u numbers and %u special characters: %s</source>
|
||||
<target>Bitte verwenden Sie mindestens %u Großbuchstaben, %u Kleinbuchstaben, %u Ziffern und %u Sonderzeichen: %s</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="XPT.passwordValidation.haveibeenpwned">
|
||||
<source>Oh no! Your password was found in a database of compromised passwords! Using a password that has been breached is extremely dangerous. Please choose a different password.</source>
|
||||
<target>Oh nein! Ihr Passwort wurde in einer Datenbank mit veröffentlichten Passwörtern gefunden! Die Verwendung eines Passworts aus einem Datenleck ist äußerst gefährlich. Bitte wählen Sie ein anderes Passwort.</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
@@ -26,6 +26,9 @@
|
||||
<trans-unit id="XPT.passwordValidation.required.summary">
|
||||
<source>Please use at least %u uppercase letters, %u lowercase letters, %u numbers and %u special characters: %s</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="XPT.passwordValidation.haveibeenpwned">
|
||||
<source>Oh no! Your password was found in a database of compromised passwords! Using a password that has been breached is extremely dangerous. Please choose a different password.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Password Validation Bundle for Contao Open Source CMS.
|
||||
*
|
||||
* @copyright Copyright (c) 2019, terminal42 gmbh
|
||||
* @author terminal42 <https://terminal42.ch>
|
||||
* @license MIT
|
||||
* @link http://github.com/terminal42/contao-password-validation
|
||||
*/
|
||||
|
||||
namespace Terminal42\PasswordValidationBundle\Validation\Validator;
|
||||
|
||||
use Contao\System;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Terminal42\PasswordValidationBundle\Exception\PasswordValidatorException;
|
||||
use Terminal42\PasswordValidationBundle\Validation\PasswordValidatorInterface;
|
||||
use Terminal42\PasswordValidationBundle\Validation\ValidationConfiguration;
|
||||
use Terminal42\PasswordValidationBundle\Validation\ValidationContext;
|
||||
|
||||
final class HaveIBeenPwned implements PasswordValidatorInterface
|
||||
{
|
||||
private $configuration;
|
||||
|
||||
public function __construct(ValidationConfiguration $configuration)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
public function validate(ValidationContext $context): bool
|
||||
{
|
||||
if (false === $this->configuration->hasConfiguration($context->getUserEntity())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$configuration = $this->configuration->getConfiguration($context->getUserEntity());
|
||||
$minDataBreaches = $configuration['haveibeenpwned'];
|
||||
if (!$minDataBreaches) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$password = $context->getPassword()->getString();
|
||||
$hash = strtoupper(sha1($password));
|
||||
$hash05 = substr($hash, 0, 5);
|
||||
$client = new Client();
|
||||
|
||||
try {
|
||||
$res = $client->request('GET', 'https://api.pwnedpasswords.com/range/'.$hash05);
|
||||
} catch (GuzzleException $e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$breaches = array_reduce(
|
||||
preg_split("/\r\n|\n|\r/", (string) $res->getBody()),
|
||||
static function ($carry, $item) use ($hash05) {
|
||||
[$hash35, $quantity] = explode(':', $item);
|
||||
$carry[$hash05.$hash35] = $quantity;
|
||||
|
||||
return $carry;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
if (\array_key_exists($hash, $breaches) && ($quantity = $breaches[$hash]) && $quantity > $minDataBreaches) {
|
||||
throw new PasswordValidatorException(sprintf($this->translate('haveibeenpwned'), $quantity));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function translate(string $key)
|
||||
{
|
||||
System::loadLanguageFile('exception');
|
||||
|
||||
return $GLOBALS['TL_LANG']['XPT']['passwordValidation'][$key];
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user