Configure password policies
Make sure you keep per-server password policy settings aligned across replicated DS servers. When per-server password policy settings differ between replicas, the results can be surprising to end users. As an example, suppose the user’s password policy depends on a password storage scheme enabled on the replica where the user changes their password and disabled on the replica where they later authenticate:
|
Adjust the default password policy
You can reconfigure the default password policy, for example, to check that passwords do not contain complete attribute values, and to prevent password reuse. The default policy is a per-server password policy.
-
Apply the changes to the default password policy:
$ dsconfig \ set-password-policy-prop \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --policy-name "Default Password Policy" \ --set password-history-count:7 \ --set password-validator:Attribute\ Value \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePassword:file /path/to/opendj/config/keystore.pin \ --no-prompt
-
Check your work:
$ dsconfig \ get-password-policy-prop \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --policy-name "Default Password Policy" \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePassword:file /path/to/opendj/config/keystore.pin \ --no-prompt Property : Value(s) ------------------------------------------:-------------------------- account-status-notification-handler : - allow-expired-password-changes : false allow-user-password-changes : true default-password-storage-scheme : PBKDF2-HMAC-SHA256 deprecated-password-storage-scheme : - expire-passwords-without-warning : false force-change-on-add : false force-change-on-reset : false grace-login-count : 0 idle-lockout-interval : 0 s last-login-time-attribute : - last-login-time-format : - lockout-duration : 0 s lockout-failure-count : 0 lockout-failure-expiration-interval : 0 s max-password-age : 0 s max-password-reset-age : 0 s min-password-age : 0 s password-attribute : userPassword password-change-requires-current-password : false password-expiration-warning-interval : 5 d password-generator : Random Password Generator password-history-count : 7 password-history-duration : 0 s password-validator : Attribute Value previous-last-login-time-format : - require-change-by-time : - require-secure-authentication : true require-secure-password-changes : true
-
Test changes to the default password policy.
For example, the following tests demonstrate the attribute value password validator. The attribute value password validator rejects a new password when the password is contained in attribute values on the user’s entry.
By default, the attribute value password validator checks all attributes, checks whether portions of the password string match attribute values, where the portions are strings of length 5, and checks the reverse of the password as well:
$ dsconfig \ get-password-validator-prop \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --validator-name Attribute\ Value \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePassword:file /path/to/opendj/config/keystore.pin \ --no-prompt Property : Value(s) -----------------------:-------------------------------------------------- check-substrings : true enabled : true match-attribute : All attributes in the user entry will be checked. min-substring-length : 5 test-reversed-password : true
Consider the attributes present on Babs Jensen’s entry:
$ ldapsearch \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePassword:file /path/to/opendj/config/keystore.pin \ --bindDN uid=bjensen,ou=People,dc=example,dc=com \ --bindPassword hifalutin \ --baseDN dc=example,dc=com \ "(uid=bjensen)" dn: uid=bjensen,ou=People,dc=example,dc=com objectClass: person objectClass: cos objectClass: oauth2TokenObject objectClass: inetOrgPerson objectClass: organizationalPerson objectClass: posixAccount objectClass: top classOfService: bronze cn: Barbara Jensen cn: Babs Jensen departmentNumber: 3001 description: Original description diskQuota: 10 GB facsimileTelephoneNumber: +1 408 555 1992 gidNumber: 1000 givenName: Barbara homeDirectory: /home/bjensen l: San Francisco mail: bjensen@example.com mailQuota: 1 GB manager: uid=trigden, ou=People, dc=example,dc=com oauth2Token: {"access_token":"123","expires_in":59,"token_type":"Bearer","refresh_token":"456"} ou: Product Development ou: People preferredLanguage: en, ko;q=0.8 roomNumber: 0209 sn: Jensen street: 201 Mission Street Suite 2900 telephoneNumber: +1 408 555 1862 uid: bjensen uidNumber: 1076
Using the attribute value password validator, passwords like
bjensen12
andbabsjensenspwd
are not valid because substrings of the password match complete attribute values:$ ldappasswordmodify \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePassword:file /path/to/opendj/config/keystore.pin \ --bindDN "uid=bjensen,ou=people,dc=example,dc=com" \ --bindPassword hifalutin \ --newPassword bjensen12 The LDAP password modify operation failed: 19 (Constraint Violation) Additional Information: The provided new password failed the validation checks defined in the server: The provided password was found in another attribute in the user entry $ ldappasswordmodify \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePassword:file /path/to/opendj/config/keystore.pin \ --bindDN "uid=bjensen,ou=people,dc=example,dc=com" \ --bindPassword hifalutin \ --newPassword babsjensenspwd The LDAP password modify operation failed: 19 (Constraint Violation) Additional Information: The provided new password failed the validation checks defined in the server: The provided password was found in another attribute in the user entry
Configure a NIST-inspired subentry policy
You can configure a password policy inspired by NIST 800-63 requirements:
-
Use a strong password storage scheme.
-
Enforce a minimum password length of 8 characters.
-
Check for matches in a dictionary of compromised passwords.
-
Do not use composition rules for password validation.
In other words, do not require a mix of special characters, upper and lower case letters, numbers, or other composition rules.
-
Do not enforce arbitrary password changes.
In other words, do not set a maximum password age.
Follow these steps to set up a replicated, NIST-inspired LDAP subentry password policy:
-
Gzip a copy of a text file of common compromised passwords, one word per line.
This example shows the gzipped text file as
/tmp/10k_most_common.gz
. After successfully updating a subentry password policy with the dictionary data, the input file is no longer required. Lists of common passwords can be found online. -
Make sure you have enabled a strong storage scheme.
Creating a password storage scheme requires access to edit the server configuration, which you might not have when creating a subentry password policy. This example therefore uses the
PBKDF2-HMAC-SHA512
storage scheme, which is enabled by default to use 10,000 iterations.This scheme is intentionally much slower and more CPU-intensive than the
PBKDF2-HMAC-SHA256
scheme with 10 iterations used by the default password policy when you install DS. Test that you have enough resources to sustain the expected peak rates of impacted operations before using a much stronger password storage scheme in your production deployment.Impacted operations include:
-
Adding or importing entries with passwords.
-
Authenticating using a password, such as simple bind.
-
Updating or resetting a password.
-
-
Make sure password policy administrators have the
subentry-write
privilege, and any required ACIs needed to write password policy subentries in the directory data.The following example grants access to password administrators. The administrator accounts are in the data where the password policy is to be stored:
$ ldapmodify \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePassword:file /path/to/opendj/config/keystore.pin \ --bindDN uid=admin \ --bindPassword password << EOF dn: cn=subentry-write privilege for administrators,dc=example,dc=com objectClass: collectiveAttributeSubentry objectClass: extensibleObject objectClass: subentry objectClass: top cn: subentry-write privilege for administrators ds-privilege-name;collective: subentry-write subtreeSpecification: {base "ou=people", specificationFilter "(isMemberOf=cn=Directory Administrators,ou=Groups,dc=example,dc=com)" } dn: dc=example,dc=com changetype: modify add: aci aci: (target="ldap:///dc=example,dc=com") (targetattr = "*||ds-pwp-password-policy-dn||pwdPolicySubentry||subtreeSpecification") (version 3.0; acl "Admins can manage entries and password policies"; allow(all) groupdn = "ldap:///cn=Directory Administrators,ou=Groups,dc=example,dc=com";) EOF
Notice here that the directory superuser,
uid=admin
, assigns privileges. Any administrator with theprivilege-change
privilege can assign privileges. However, if the administrator can update administrator privileges, they can assign themselves thebypass-acl
privilege. Then they are no longer bound by access control instructions, including both user data ACIs and global ACIs. For this reason, do not assign theprivilege-change
privilege to normal administrator users. -
Create the password policy as one of the password policy administrators:
$ ldapmodify \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePassword:file /path/to/opendj/config/keystore.pin \ --bindDN uid=kvaughan,ou=people,dc=example,dc=com \ --bindPassword bribery << EOF dn: cn=NIST inspired policy,dc=example,dc=com objectClass: top objectClass: subentry objectClass: ds-pwp-password-policy objectClass: ds-pwp-validator objectClass: ds-pwp-length-based-validator objectClass: ds-pwp-dictionary-validator cn: NIST inspired policy ds-pwp-password-attribute: userPassword ds-pwp-default-password-storage-scheme: PBKDF2-HMAC-SHA512 ds-pwp-length-based-min-password-length: 8 ds-pwp-dictionary-data:<file:///tmp/10k_most_common.gz subtreeSpecification: {base "ou=people", specificationFilter "(objectclass=person)" } EOF
After successfully adding the policy with the dictionary data, you can delete the input file.
-
Check the password policy works appropriately.
The following example shows a rejected password modification:
$ ldappasswordmodify \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePassword:file /path/to/opendj/config/keystore.pin \ --bindDN "uid=bjensen,ou=people,dc=example,dc=com" \ --bindPassword hifalutin \ --newPassword secret12 The LDAP password modify operation failed: 19 (Constraint Violation) Additional Information: The provided new password failed the validation checks defined in the server: The provided password was found in another attribute in the user entry
The following example shows an accepted password modification:
$ ldappasswordmodify \ --hostname localhost \ --port 1636 \ --useSsl \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePassword:file /path/to/opendj/config/keystore.pin \ --bindDN "uid=bjensen,ou=people,dc=example,dc=com" \ --bindPassword hifalutin \ --newPassword aET1OjQeVJECSMgxDPs3U6In The LDAP password modify operation was successful
Create a per-server password policy
This example adds a per-server password policy for new users who have not yet used their credentials to bind:
-
Create the new password policy:
$ dsconfig \ create-password-policy \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --policy-name "New Account Password Policy" \ --set default-password-storage-scheme:PBKDF2-HMAC-SHA256 \ --set force-change-on-add:true \ --set password-attribute:userPassword \ --type password-policy \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePassword:file /path/to/opendj/config/keystore.pin \ --no-prompt
As per-server password policies are not replicated, repeat this step on all replica directory servers.
-
Check your work:
$ dsconfig \ get-password-policy-prop \ --hostname localhost \ --port 4444 \ --bindDN uid=admin \ --bindPassword password \ --policy-name "New Account Password Policy" \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePassword:file /path/to/opendj/config/keystore.pin \ --no-prompt Property : Value(s) ------------------------------------------:------------------- account-status-notification-handler : - allow-expired-password-changes : false allow-user-password-changes : true default-password-storage-scheme : PBKDF2-HMAC-SHA256 deprecated-password-storage-scheme : - expire-passwords-without-warning : false force-change-on-add : true force-change-on-reset : false grace-login-count : 0 idle-lockout-interval : 0 s last-login-time-attribute : - last-login-time-format : - lockout-duration : 0 s lockout-failure-count : 0 lockout-failure-expiration-interval : 0 s max-password-age : 0 s max-password-reset-age : 0 s min-password-age : 0 s password-attribute : userPassword password-change-requires-current-password : false password-expiration-warning-interval : 5 d password-generator : - password-history-count : 0 password-history-duration : 0 s password-validator : - previous-last-login-time-format : - require-change-by-time : - require-secure-authentication : false require-secure-password-changes : false
-
Change the user’s password policy after the password is successfully updated.
For instructions on assigning a per-server password policy, refer to Assign a password policy to a user.