Password blacklist with Forgerock IDM 5
03 Sep 2017NIST recently released new password guidelines, one of those being:
"When processing requests to establish and change memorized secrets, verifiers SHALL compare the prospective secrets against a list that contains values known to be commonly-used, expected, or compromised."
In this blog post we will implement a blacklist mechanism using IDM 5 to prevent users from choosing a password that is easily vulnerable to a dictionary attack.
Implementation
Define a password blacklist
Let's create our list of password that need to be banned:
$ cd openidm/bin/default/scripts
$ touch policy-passwordBlacklist.js
Edit the newly created script with the following code:
(function () {
exports.passwordList = [
"123456",
"123456789",
"qwerty",
"12345678",
"111111",
"1234567890",
"1234567",
"password",
"123123",
"987654321",
"qwertyuiop",
"mynoob",
"123321",
"666666",
"18atcskd2w",
"7777777",
"1q2w3e4r",
"654321",
"555555",
"3rjs1la7qe",
"google",
"1q2w3e4r5t",
"123qwe",
"zxcvbnm",
"1q2w3e"
]
}());
Password policy configuration
Open the default IDM 5 policy script file:
$ vi openidm/bin/defaults/script/policy.js
Inside the policies array variable, add a new policy reference to make it available for password verification:
{
"policyId" : "not-in-most-common-password-list",
"policyExec" : "notInMostCommonPasswordList",
"clientValidation": true,
"validateOnlyIfPresent": true,
"policyRequirements" : ["NOT_IN_MOST_COMMON_PASSWORD_LIST"]
}
Then create the actual function that will perform the check:
policyFunctions.notInMostCommonPasswordList = function(fullObject, value, params, prop) { var passwordListPolicy = require("policy-passwordBlacklist"); var isRequired = _.find(this.failedPolicyRequirements, function (fpr) { return fpr.policyRequirement === "REQUIRED"; }), isNonEmptyString = (typeof(value) === "string" && value.length), valueIsNotInList = isNonEmptyString ? (passwordListPolicy.passwordList.indexOf(value) < 0) : false; if ((isRequired || isNonEmptyString) && !valueIsNotInList) { return [ { "policyRequirement" : "NOT_IN_MOST_COMMON_PASSWORD_LIST" } ]; } return []; };
Next step is to tell IDM to use this policy for the password attribute of the user object.
$ vi openidm/conf/managed.json
Edit the user object by applying the the new policy to the password attribute:
"password" : {
"title" : "Password",
"type" : "string",
"viewable" : false,
"searchable" : false,
"minLength" : 8,
"userEditable" : true,
"encryption" : {
"key" : "openidm-sym-default"
},
"scope" : "private",
"isProtected": true,
"policies" : [
{
"policyId" : "not-in-most-common-password-list"
},
{
"policyId" : "cannot-contain-others",
"params" : {
"disallowedFields" : [
"userName",
"givenName",
"sn"
]
}
}
]
}
Last thing, we want to display a nice error message in case a user tries to choose one of the banned password.
$ vi openidm/ui/selfservice/default/locales/en/translation.json
Edit the common.form.validation object by adding our policy reference:
"common": {
"form": {
...
"validation" : {
"NOT_IN_MOST_COMMON_PASSWORD_LIST": "This password is too weak. Please choose another password.",
...
}
}
Let's test
We can test the policy by performing a password reset flow with a choosen user.
After receiving an email with a link inside, IDM asks me to choose another password.
If I choose a banned password, the policy is triggered and prevents the password change: