open-vault/website/content/docs/concepts/password-policies.mdx

368 lines
14 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
layout: docs
page_title: Password Policies
description: >-
Password policies are used in some secret engines to allow users to define how passwords are generated
for dynamic & static users within those engines.
---
# 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](/docs/concepts/policies) other than sharing similar names.
Password policies are available in Vault version 1.5+. [API docs can be found here](/api-docs/system/policies-password).
!> Password policies are an advanced usage of Vault. This generates credentials for external systems
(databases, LDAP, AWS, etc.) and should be used with caution.
## 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](#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](/img/vault-password-policy-flow.svg)](/img/vault-password-policy-flow.svg)
## 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](https://golang.org/pkg/crypto/rand/) (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](https://en.wikipedia.org/wiki/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](https://en.wikipedia.org/wiki/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.
<details>
<summary>No Minimum Characters</summary>
```hcl
rule "charset" {
charset = "abcdefghijklmnopqrstuvwxyz"
}
rule "charset" {
charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
}
rule "charset" {
charset = "0123456789"
}
rule "charset" {
charset = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
}
```
</details>
<details>
<summary>1 uppercase, 1 lowercase, 1 numeric</summary>
```hcl
rule "charset" {
charset = "abcdefghijklmnopqrstuvwxyz"
min-chars = 1
}
rule "charset" {
charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
min-chars = 1
}
rule "charset" {
charset = "0123456789"
min-chars = 1
}
rule "charset" {
charset = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
}
```
</details>
<details>
<summary>1 uppercase, 1 lowercase, 1 numeric, 1 from all ASCII characters</summary>
```hcl
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
}
```
</details>
<details>
<summary>1 uppercase, 1 lowercase, 1 numeric, 1 from <code>!@#$</code></summary>
```hcl
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 = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
}
```
</details>
[![Password Policy Performance](/img/vault-password-policy-performance.svg)](/img/vault-password-policy-performance.svg)
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.
<details>
<summary><b>Click here for more details on password generation probabilities</b></summary>
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](/img/vault-password-policy-chance.svg)](/img/vault-password-policy-chance.svg)
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`](/api-docs/system/policies-password) endpoint to see how much time the policy takes to
produce passwords.
</details>
## Password Policy Syntax
Password policies are defined in [HCL](https://github.com/hashicorp/hcl) or JSON which defines
the length of the password and a set of rules a password must adhere to.
See the [API docs](/api-docs/system/policies-password) 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:
```hcl
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 `!@#$%^&*`:
```hcl
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:
```hcl
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.
~> After combining and de-duplicating charsets, the length of the charset that candidate passwords
are generated from must be no longer than 256 characters.
#### 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.
Please note that the JSON output returned may be escaped for the special and control characters such as <,>,& etc as per the JSON specification.
- `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
```hcl
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:
```hcl
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.
## Tutorial
Refer to [User Configurable Password Generation for Secret
Engines](https://learn.hashicorp.com/vault/secrets-management/password-policies)
for a step-by-step tutorial.