»Password Policies

A password policy is a set of instructions on how to generate a password, similar to other password generators. These password policies are used in a subset of secret engines to allow you to configure how a password is generated for that engine. Not all secret engines utilize password policies, so check the documentation for the engine you are using for compatibility.

Note: Password policies are unrelated to Policies other than sharing similar names.

Password policies are available in Vault version 1.5+. API docs can be found here.

»Design

Password policies fundamentally have two parts: a length, and a set of rules that a password must adhere to. Passwords are randomly generated from the de-duplicated union of charsets found in all rules and then checked against each of the rules to determine if the candidate password is valid according to the policy. See Candidate Password Generation for details on how passwords are generated prior to being checked against the rule set.

A rule is an assertion upon a candidate password string that indicates whether or not the password is acceptable. For example: a "charset" rule states that a password must have at least one lowercase letter in it. This rule will reject any passwords that do not have any lowercase letters in it.

Multiple rules may be specified within a policy to create more complex rules, such as requiring at least one lowercase letter, at least one uppercase letter, and at least one number.

The flow looks like:

Vault Password Policy Flow

»Candidate Password Generation

How a candidate password is generated is extremely important. Great care must be placed to ensure that passwords aren't created in a way that can be exploited by threat actors. This section describes how we generate passwords within password policies to ensure that passwords are generated as securely as possible.

To generate a candidate password, three things are needed:

  1. A cryptographically secure random number generator (RNG).
  2. A character set (charset) to select characters from.
  3. The length of the password.

At a high level, we use our RNG to generate N numbers that correspond to indices into the charset array where N is the length of the password we wish to create. Each value returned from the RNG is then used to extract a character from the charset into the password.

For example, let's generate a password of length 8 from the charset abcdefghij:

The RNG is used to generate 8 random values. For our example let's say those values are:

[3, 2, 0, 8, 7, 3, 5, 1]

Each of these values is an index into the charset array:

[3, 2, 0, 8, 7, 3, 5, 1] => [d, c, a, i, h, d, f, b]

This gives us our candidate password: dcaihdfb which can then be run through the rules of the policy.

In a real world scenario, the values in the random array will be between [0-255] as that is the range of values that a single byte can be. The value is restricted to the size of the charset array by using the modulo operation to prevent referencing a character outside the bounds of the charset. However this can introduce a problem with bias.

»Preventing Bias

When using the modulo operation to generate a password, you must be very careful to prevent the introduction of bias. When generating a random number between [0-255] for a charset that has a length that isn't evenly divisible into 256, some of the first characters in the charset may be selected more frequently than the remaining characters.

To demonstrate this, let's simplify the math. Assume that we have a charset of length 10: abcdefghij. Let's also assume that our RNG generates values [0-25]. The first 10 values 0-9 correspond to each character in our charset. The next 10 values 10-19 also correspond to each character in our charset. However, the next 6 values 20-25 correspond to only the first 6 characters in the charset. This means that those 6 characters abcdef can be selected more often than the last 4 characters ghij.

In order to prevent this from happening, we calculate the maximum value that we can allow an index to be. This is based on the length of the charset we are selecting from. In the example above, the maximum index value we should allow is 19 as that represents the largest integer multiple of the length of the charset array that is less than the maximum value that our RNG can generate. When our RNG generates any values larger than our maximum allowed value, that number is ignored and we continue to the next number. Passwords do not lose any length because we continue generating numbers until the password is fully filled in to the length requested.

»Performance Characteristics

Characterizing password generation performance with this model is heavily dependent on the policy configuration. In short, the more restrictive the policy, the longer it will take to generate a password. This generalization isn't always true, but is a general guideline. The performance curve can be generalized:

(time to generate a candidate password) * (number of candidate passwords generated)

Where the number of times a candidate password needs to be generated is a function of how likely a given candidate password does not pass all of the rules.

Here are some example policy configurations with their performance characteristics below. Each of these policies have the same charset that candidate passwords are generated from (94 characters). The only difference is the minimum number of characters for various character subsets.

No Minimum Characters
rule "charset" {
  charset = "abcdefghijklmnopqrstuvwxyz"
}
rule "charset" {
  charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
}
rule "charset" {
  charset = "0123456789"
}
rule "charset" {
  charset = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
}
1 uppercase, 1 lowercase, 1 numeric
rule "charset" {
  charset = "abcdefghijklmnopqrstuvwxyz"
  min-chars = 1
}
rule "charset" {
  charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  min-chars = 1
}
rule "charset" {
  charset = "0123456789"
  min-chars = 1
}
rule "charset" {
  charset = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
}
1 uppercase, 1 lowercase, 1 numeric, 1 from all ASCII characters
rule "charset" {
  charset = "abcdefghijklmnopqrstuvwxyz"
  min-chars = 1
}
rule "charset" {
  charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  min-chars = 1
}
rule "charset" {
  charset = "0123456789"
  min-chars = 1
}
rule "charset" {
  charset = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
  min-chars = 1
}
1 uppercase, 1 lowercase, 1 numeric, 1 from !@#$
rule "charset" {
  charset = "abcdefghijklmnopqrstuvwxyz"
  min-chars = 1
}
rule "charset" {
  charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  min-chars = 1
}
rule "charset" {
  charset = "0123456789"
  min-chars = 1
}
rule "charset" {
  charset = "!@#$"
  min-chars = 1
}
# Fleshes out the rest of the symbols but doesn't add any required characters
rule "charset" {
  charset = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
}

Password Policy Performance

As more characters are generated, the amount of time increases (as seen in No Minimum Characters). This upward trend can be dwarfed by restricting charsets. When a password is short, the chances of a character being selected from a subset is smaller. For instance, if you have a 1 character password from the charset abcde the chances of selecting c from it is 1/5. However if you have a 2 character password, the chances of selecting c at least once are greater than 1/5 because you have a second chance to select c from the charset.

In these examples, as the length of the password increases, the amount of time to generate a password trends down, levels off, and then slowly increases. This is a combination of the two effects listed above: increasing time to generate more characters vs the chances of the character subsets being selected. When a single subset is very small (such as !@#$) the chances of it being selected are much smaller (4/94) than if the subset is larger (26/94 for lowercase characters). This can result in a dramatic loss of performance.

Click here for more details on password generation probabilities

In the examples above, the charset being used to generate candidate passwords is 94 characters long. Randomly choosing a given character from the 94 character charset has a 1/94 chance. Choosing a single character from it after N tries (where N is the length of the password) is 1-(1-1/94)^N.

If we expand this to look at a subset of characters (such as lowercase characters) the chances of selecting a character from that subset is 1-(1-L/94)^N where L is the length of the subset. For lowercase characters, we get a probability of 1-(1-26/94)^N.

If we do this for uppercase letters as well as numbers, then we get a combined probability curve:

p = (1-(1-26/94)^N) * (1-(1-26/94)^N) * (1-(1-10/94)^N)

Chance of Generating a Good Password - 1

It should be noted that this probability curve only applies to this specific policy. To understand the performance characteristics of a given policy, you should run your policy with the generate endpoint to see how much time the policy takes to produce passwords.

»Password Policy Syntax

Password policies are defined in HCL or JSON which defines the length of the password and a set of rules a password must adhere to.

See the API docs for examples of the commands to save/read/etc. password policies

Here is a very simple policy which generates 20 character passwords from lowercase characters:

length = 20
rule "charset" {
  charset = "abcdefghijklmnopqrstuvwxyz"
}

Multiple rules may be specified, including multiple rules of the same type. For instance, the following policy will generate a 20 character password with at least one lowercase letter, at least one uppercase letter, at least one number, and at least one symbol from the set !@#$%^&*:

length = 20
rule "charset" {
  charset = "abcdefghijklmnopqrstuvwxyz"
  min-chars = 1
}
rule "charset" {
  charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  min-chars = 1
}
rule "charset" {
  charset = "0123456789"
  min-chars = 1
}
rule "charset" {
  charset = "!@#$%^&*"
  min-chars = 1
}

At least one charset must be specified for a policy to be valid. In order to generate a password, a charset must be available to select characters from and password policies do not have a default charset. The following policy is NOT valid and will be rejected:

length = 20

»Configuration & Available Rules

»length Parameter

  • length (int: <required>) - Specifies how long the generated password will be. Must be >= 4.

Length is not a rule. It is the only part of the configuration that does not adhere to the guess- and-check approach of rules.

»Rule charset

Allows you to specify a minimum number of characters from a given charset. For instance: a password must have at least one lowercase letter. This rule also helps construct the charset that the password generation utilizes. In order to generate a password, a charset must be specified.

If multiple charsets are specified, all of the charsets will be combined and de-duplicated prior to generating any candidate passwords. Each individual charset rule will still need to be adhered to in order to successfully generate passwords.

»Parameters

  • charset (string: <required>) – A string representation of the character set that this rule observes. Accepts UTF-8 compatible strings. All characters within the string must be printable.
  • min-chars (int: 0) - Specifies a minimum number of characters required from the charset specified in this rule. For example: if min-chars = 2, the password must have at least 2 characters from charset.

»Example

length = 20
rule "charset" {
  charset = "abcde"
  min-chars = 1
}
rule "charset" {
  charset = "01234"
  min-chars = 1
}

This policy will generate passwords from the charset abcde01234. However, the password must have at least one character that is from abcde and at least one character from 01234. If charsets overlap between rules, the charsets will be de-duplicated to prevent bias towards the overlapping set. For instance: if you have two charset rules: abcde & cdefg, the charset abcdefg will be used to generate candidate passwords, but a least one character from each abcde & cdefg must still appear in the password.

If min-chars is not specified (or set to 0) then this charset will not have a minimum required number of characters, but it will be used to select characters from. Example:

length = 8
rule "charset" {
  charset = "abcde"
}
rule "charset" {
  charset = "01234"
  min-chars = 1
}

This policy generates 8 character passwords from the charset abcde01234 and requires at least one character from 01234 to be in it, but does not require any characters from abcde. The password 04031945 may result from this policy, even though no alphabetical characters are in it.

»Learn

Refer to User Configurable Password Generation for Secret Engines for a step-by-step tutorial.