New in Symfony 4.3: Compromised password validator
April 5, 2019 • Published by Javier Eguiluz
Warning: This post is about an unsupported Symfony version. Some of this information may be out of date. Read the most recent Symfony Docs.
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:
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 are closed.
To ensure that comments stay relevant, they are closed for old posts.
Nice, but from other hand isn't this bit risky to ask remote service about your password to build up their brute force DB? :D
So the NotPwned passes the raw password to haveibeenpwned.com API. I am not sure this is something the backend should handle. I think should really happen on the client side.
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.
Hi @Piotr and @Ersin,
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
The implementation does not send the password to haveibeenpowned, it sends a hash of the 5 first characters, and then compares the password to a list returned by the API ("k-anonymity model"). So it is safe :) That's a great addition!
@Oliver +1, right I didn't check the PR before commenting. Thx for heads up :)
Probably it would be nice to disable it on dev environment. My fixtures would be crazy right now :)
Thank you all for your comments. I've just updated the blog post with more technical details about how does the password validation work.
@Romaric
No. It sends 5 first characters of the password's SHA-1 hash, not hash of password's first 5 characters.
In my opinion the validator name "NotPwned" is very developer-unfriendly. People who read through my code and don't know that service are pretty much confused what it does.
Why not give it a more speaking name like NotCompromised or NotListedOnHaveibeenpwned?
What do others think about this?
@Piotr Karszny you validate your fixtures?
@Thomas Schulz I agree, maybe it should have been @NotBreached or @NotCompromised.
I think it's a great idea to provide a validation component for passwords into the framework using this functionality. I just wonder why make it a specific Symfony component as many frameworks out there have validation components.
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.
Nice job. Also may be some user doesnt understand that they cannot choose her own pw.
Good option! what I do not understand, is that if you have put this, because yours have removed or deprecated checkMX, I suppose that in the end the same thing will happen.
Thanks so much!
@Michelangelo van Dam there's nothing "Symfony specific" here. This is shipped in a standalone component, exactly like yours, except that this is backed by all standard maintainance processes of the project.
@Nicolas Grekas Gotcha! Thank you for clarifying.
Computerphile on youtube also has a good video on the topic: https://www.youtube.com/watch?v=hhUb5iknVJs
@Thomas Schulz You can simply extend the constraint and the validator if that bothers You that much.