How to create your own password policy
Prerequisite: You must be familiar with the Syntax used in Tutorials and have already created an extension.
Possible policies
Here are some ideas, they are not available in iTop. But you can write an extension to implement the policy and have it controlled by iTop automatically.
Blacklist Policy
Some passwords are so easy to guess that they are not efficient. There exist lists of weak passwords, and you could write a policy which would check that a user defined password is not in that list.
It is quite easy to code, so we will use it as example in this tutorial
Password different from previous passwords
This one, is a little more complicated, because :
-
iTop store only passwords hash,
-
and the salt is both random and hard to extract
-
(it is stored alongside the hash, and how to separate the salt and the hash depends upon the hash algorithm used.
-
So we write here a summary on own to write this validator:
-
The validator must implements
iApplicationObjectExtension::OnDBUpdate()
in addition to the\UserLocalPasswordValidator
interface-
in order to store the previous passwords hashes into a custom classe dedicated to this role
-
-
The check must be performed using a clear text password against this list of previous hashes.
-
In other words, it cannot be done by comparing two hases.
-
-
it seem reasonable to limit the number of previous stored hashes.
-
it can be made configurable
-
How to write an extension
You can add as many validator as you want, by making them
implements the UserLocalPasswordValidator
interface.
class MyCustomPasswordValidator implements UserLocalPasswordValidator { // your code here ... }
To perform the validation, just return a
\ValidatePassword
instance.
-
if the password is valid return
new \UserLocalPasswordValidity(true)
-
if the password is not valid, return
new \UserLocalPasswordValidity(false, $sMessage)
.-
$sMessage
must be an already translated message that will be displayed to the end user. Per convention, please use this form'Error:UserLocalPasswordValidator:<yourClassName>/ValidationFailed
' replacing<yourClassName>
with your class name.
-
Example: write a Blacklist validator
Create an extension with this class:
class UserPasswordPolicyBlacklist implements UserLocalPasswordValidator { public function __construct() { } /** * @param string $proposedValue * @param UserLocal $oUserLocal * @param Config $config * * @return UserLocalPasswordValidity */ public function ValidatePassword($proposedValue, UserLocal $oUserLocal, $config) { $sBlacklistFile = APPROOT.'data/passwordBlacklist.txt'; $handle = @fopen($sBlacklistFile, "r"); if (! $handle) { return new UserLocalPasswordValidity( false, 'Unknown error : Failed to read the password blacklist.' ); } try { while (($sBlackListPwd = fgets($handle, 4096)) !== false) { if (trim($sBlackListPwd) == trim($proposedValue)) { $sMessage = Dict::S( 'Error:UserLocalPasswordValidator:UserPasswordPolicyBlacklist/ValidationFailed' ); return new UserLocalPasswordValidity( false, $sMessage ); } } if (!feof($handle)) { return new UserLocalPasswordValidity( false, 'Unknown error : Failed to read the password blacklist until the end.' ); } } finally { fclose($handle); } return new UserLocalPasswordValidity(true); } }
data/passwordBlacklist.txt
has to be composed of
one forbidden password per line.
You may per example use this file : https://github.com/danielmiessler/SecLists/blob/master/Passwords/darkweb2017-top10000.txt
it contains the 10.000 most frequent passwords.