New in Symfony 4.3: Compromised password validator

Warning: This post is about an unsupported Symfony version. Some of this information may be out of date. Read the most recent Symfony Docs.
Contributed by
Kévin Dunglas
in #27738.
A data breach is the intentional or unintentional release of secure or private/confidential information to an untrusted environment. The list of data breaches increases every day and, just in the first half of 2018, about 4.5 billion records were exposed, including user passwords.
Users that set their password to any of the publicly exposed passwords are a serious security problem for web sites and applications. That's why services like have i been pwned? allow you to check if your password is compromised.
In Symfony 4.3, we've added a new NotCompromisedPassword
constraint to
validate that the given password hasn't been compromised:
- Annotations
- YAML
- XML
- PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// src/Entity/User.php
namespace App\Entity;
use Symfony\Component\Validator\Constraints as Assert;
class User
{
// ...
/**
* @Assert\NotCompromisedPassword
*/
protected $rawPassword;
}
1 2 3 4 5
# config/validator/validation.yaml
App\Entity\User:
properties:
rawPassword:
- NotCompromisedPassword
1 2 3 4 5 6 7 8 9 10 11 12
<!-- config/validator/validation.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
<class name="App\Entity\User">
<property name="rawPassword">
<constraint name="NotCompromisedPassword"></constraint>
</property>
</class>
</constraint-mapping>
1 2 3 4 5 6 7 8 9 10 11 12 13
// src/Entity/User.php
namespace App\Entity;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints as Assert;
class User
{
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint('rawPassword', new Assert\NotCompromisedPassword());
}
}
Internally, the constraint makes an HTTP request to the API provided by the
haveibeenpwned.com
website. In the request, the validator doesn't send the
raw password but only the few first characters of the result of encoding it
using SHA-1.
For example, if the raw password is test
, the SHA-1 hash is
a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
and the validator only sends
a94a8
to haveibeenpwned.com
(the first five characters of the SHA-1
hash). This is called "k-anonymity password validation" and is fully
explained in this blog post by Cloudflare.
The HTTP request is made with the new HttpClient component added in Symfony 4.3 and which will we introduced soon in a dedicated blog post.
Help the Symfony project!
As with any Open-Source project, contributing code or documentation is the most common way to help, but we also have a wide range of sponsoring opportunities.
Comments
Comments are closed.
To ensure that comments stay relevant, they are closed for old posts.
Also the instead of the database, I think the network/proxy of the servers can be compromised equally and fake CA and Domain and raw password can be exposed.
I am not sure I understand.
look at the merged pull-request (https://github.com/symfony/symfony/pull/27738).
You will see, that the raw rassword is hashed using sha1 and only the first few characters of that hash to the service (https://blog.cloudflare.com/validating-leaked-passwords-with-k-anonymity/).
So there will be no filling their DB with your passwords :D
That's a great addition!
> The implementation does not send the password to haveibeenpowned, it sends a hash of the 5 first characters
No. It sends *5 first characters of the password's SHA-1 hash*, not hash of password's first 5 characters.
Why not give it a more speaking name like NotCompromised or NotListedOnHaveibeenpwned?
What do others think about this?
@Thomas Schulz I agree, maybe it should have been @NotBreached or @NotCompromised.
I provided a Composer package "dragonbe/hibp" for the purpose making password validation available to everyone independent from the framework. And to prevent the rebuilding of logic, wouldn't it be better if we have one common base component with each framework implementing that base component? For details, see https://packagist.org/packages/dragonbe/hibp and yes, I accept PR's to make it better suited for all the frameworks.
Thanks so much!
https://www.youtube.com/watch?v=hhUb5iknVJs