neopass
Encourage better passwords.
Neopass is a password validation and generation tool kit built on plugins.
Under Development
This package is currently under development and the interface is unstable. While the package remains at version 0.x.y
, the minor version will be updated when known breaking changes occur.
Contents
- Installation
- Basics
- External Generators
- Built-in Validators
- Configuring Validators
- Custom Validators
- Optional Rules
- Plugins
- Override Configured Chains
Installation
npm install neopass
Basics
Password Generation
Neopass can generate random strings using configured generators. By default, there are three generators available:
random
: produces n random characters from a character set of typable characters uppercase, lowercase, digit, and special.
letters-numbers
: produces n random characters from a character set of uppercase, lowercase, and digit.
hex
: produces n random bytes in hexidecimal form.
console.log'random:', pass1 console.log'letters-numbers', pass2 console.log'hex', pass3
Output:
random: |6Tc/]>4KY:5
letters-numbers: WIW71lZEuUsN
hex: 9b9ede126e4f2556fe1717dc
Registered Generators
A list of registered generators can be obtained with the following call:
console.log'generators:', generators
Output:
generators: [
{ name: 'random', title: 'Random', units: 'char' },
{ name: 'letters-numbers', title: 'Letters & Numbers', units: 'char' },
{ name: 'hex', title: 'Hexidecimal', units: 'byte' }
]
The Validation Chain
The validation chain reports errors from each configured validator.
// Validate a password.console.log'errors:', errors
Output:
errors: [
{
name: 'length',
msg: 'password length should be between 10 and 72 characters, inclusive',
score: 0.7
},
{
name: 'classes',
msg: 'missing uppercase character',
meta: 'u'
},
{
name: 'classes',
msg: 'missing one of digit, special',
meta: 'ds'
}
]
Generator Retries
When generating passwords, it might be important for you to make sure that the generated password will pass your defined validation chain. Depending on how the chain is configured, random strings produced by password generation might occasionally result in a password that fails a validation step. You can specify generator retries
to retry generation a number of times to mitigate cases where this may occur.
console.logpass // j47VXGqqq3// Oops! There are three 'q' together which violates the run validator. // Specify up to five retries to regenerate passwords that fail validation.console.logsafePass // iUG6r76ecR
If the number of retries is exceeded, generate
will throw an error, which would suggest that your validation rules might be too strict, or perhaps the selected generator is not appropriate for your use case.
The Evaluation Chain
The evaluation chain reports a password's strength based on configured evaluators.
console.log'result:', result
Output:
result: {
strength: 0.44099999999999995,
warnings: [
{
name: 'length',
msg: 'password length should be between 10 and 72 characters, inclusive',
score: 0.7
},
{
name: 'classes',
msg: 'missing one of digit, special',
meta: 'ds'
}
]
}
Assessing password strength is subjective and can be rather arbitrary. A good philosopy is to define evaluators that represent a near-ideal or practically-perfect password, so that there is a good range between low-strength and high-strength passwords, and you're not representing an overly-optimistic strength assessment.
Here's an example that makes some attempt at a reasonable strength evaluation:
Bad:
neo.evaluate'Denver2016'
{ strength: 0.348878690061731,
warnings:
[ { name: 'length',
msg: 'password length should be between 20 and 72 characters, inclusive',
score: 0.5,
meta: 'min' },
{ name: 'entropy',
msg: 'password is either too short or not complex enough',
score: 0.9303431734979493 },
{ name: 'topology',
msg: 'password matches vulnerable pattern topology' } ] }
Still bad:
neo.evaluate'Denver2016!'
{ strength: 0.41250000000000003,
warnings:
[ { name: 'length',
msg: 'password length should be between 20 and 72 characters, inclusive',
score: 0.55,
meta: 'min' },
{ name: 'topology',
msg: 'password matches vulnerable pattern topology' } ] }
Not great:
neo.evaluate'D3nv3r2016!'
{ strength: 0.55,
warnings:
[ { name: 'length',
msg: 'password length should be between 20 and 72 characters, inclusive',
score: 0.55,
meta: 'min' } ] }
Good:
neo.evaluate'D3nv3r2016!and17$'
{ strength: 0.85,
warnings:
[ { name: 'length',
msg: 'password length should be between 20 and 72 characters, inclusive',
score: 0.85,
meta: 'min' } ] }
Great?:
neo.evaluate'correct Horse battery staple'
{ strength: 1, warnings: [] }
Strength assessment is a bit of both art and science, and it won't assure a great password. However it is a good tool to help users to create better passwords in general.
Passphrase Detection
Passphrases are long passwords typically comprised of multiple words. These
are considered to be more secure than shorter, mixed-class passwords. If configured,
neopass
will detect a passphrase and bypass additional validation in the validation chain.
/** * The below password would normally fail the 'classes' validator * as configured because there is no digit/special character. */console.log'errors:', errors
Output:
errors: []
External Generators
Memorable Generator
The Memorable Generator generates an easy-to-remember password:
npm install @neopass/memorable-generator @neopass/words-by-length-view
Optional:
npm install @neopass/wordlist
const neopass = const WordsByLengthView = const MemorableGenerator = const wordListSync = /** * We're using @neopass/wordlist to feed the view, but any * word list of sufficient size will do. */const view = const neo = const pass = neoconsole
Output:
password: smugly$Luminous7
See Memorable Generator on npm.
Built-in Validators
ClassesValidator
Validates a password based on its character classes, mainly u
(uppercase), l
(lowercase), d
(digit), and s
(special character).
console.logerrors
Output:
[ { name: 'classes',
msg: 'missing uppercase character',
meta: 'u' },
{ name: 'classes',
msg: 'missing one of digit, special',
meta: 'ds' } ]
CommonValidator
Validates a password based on whether it's included in a common passwords list.
Note that CommonValidator
requires a password list to be given in its options, so it is not short-form configurable.
console.logerrors
Output:
[ { name: 'common',
msg: 'password found in a common vulnerable password list' } ]
DepthValidator
Validates a password based on its character depth, derived from its classes. For example, a password Simple32
has the classes u
, l
, and d
, which means its depth is u=26 + l=26 + d=10 = 62
.
console.logerrors
Output:
[ { name: 'depth',
msg: 'password needs more class complexity (uppercase, lowercase, digit, special)',
score: 0.41935483870967744 } ]
EntropyValidator
Validates a password based on its class entropy, derived from its character depth. The entropy is calculated as Math.log2(depth) * length
, resulting in a number of bits for the entire password string. The class entropy differs from shannon entropy in that the bits per symbol is assumed based on which character classes are represented in the password. This validator is similar to the DepthValidator in that its value is based on which classes are present in the password, but adds the effect of having awareness of password length. In other words, a password may have several character classes represented, yet still be too short to pass validation.
console.logerrors
Output:
[ { name: 'entropy',
msg: 'password is either too short or not complex enough',
score: 0.9238859449215395 } ]
LengthValidator
Validates a password based on its length.
Configuration:
console.logerrors
Output:
[ { name: 'length',
msg: 'password length should be between 10 and 72 characters, inclusive',
score: 0.6,
meta: 'min' } ]
RunValidator
Validates whether a password has runs
of a single character, such as in pAAAsword
.
console.logerrors
Output:
[ { name: 'run',
msg: 'password contains at least 1 character run(s)',
meta: 1 } ]
SequenceValidator
Validates whether a password has sequences of characters, such as in ABCDlmnop
.
console.logerrors
Output:
[ { name: 'sequence',
msg: 'password contains at least 2 character sequence(s)',
meta: 2 } ]
ShannonValidator
Validates a password based on its Shannon entropy. Shannon entropy is given in raw bits per symbol. The validator multiplies this by the password length resulting in a number representing the entropy for the entire string: shannon(password) * length
. Shannon entropy differs from class entropy in that no assumption is made about which character classes are represented by the password; only the symbols in the password itself count toward the bits per symbol calculation.
shannon('aaaa') // 0 bits per symbol
shannon('ab') // 1 bit per symbol
shannon('abcd') // 2 bits per symbol
shannon('01234567') // 3 bits per symbol
shannon('0123456789abcdef') // 4 bits per symbol
console.log'errors1:', errors1console.log'errors2:', errors2console.log'errors3:', errors3
Output:
errors1: [ { name: 'shannon', msg: 'password is too simple', score: 0 } ]
errors2: [ { name: 'shannon', msg: 'password is too simple', score: 0.4655 } ]
errors3: [ { name: 'shannon', msg: 'password is too simple', score: 0.9457 } ]
TopologyValidator
Validates a password according to whether it conforms to certain common topology patterns.
console.logerrors
Output:
[ { name: 'topology',
msg: 'password matches vulnerable pattern topology' } ]
Configuring Validators
Short Form
The short-form configuration uses a string to reference the plugin and its options/arguments. In this case, options and arguments can be primitive types such as number
, string
, boolean
, etc.
Long Form
The long-form configuration uses an object to reference the plugin and its options/arguments. Options and arguments can be objects and arrays, in addition to other types, such as RegExp
.
Custom Validators
Custom validators can be used by either authoring a validator plugin or using in-line custom function validators:
/** * Create a custom validator function. */ // Configure neopass. console.log'errors:', errors
Output:
errors: [
{
name: 'custom-depth',
msg: 'password not complex enough',
score: 0.41935483870967744
}
]
Custom validators can also be used in the evaluation chain.
Optional Rules
If you want optional rules - that is, rules where some errors are treated as warnings - use the verify
method which joins validation and evaluation. Validation failures are returned as errors and evaluation failures are returned as warnings.
console.logresult
Output:
{
errors: [],
warnings: [
{
name: 'shannon',
msg: 'password is too simple',
score: 0.9756025296523007
},
{
name: 'entropy',
msg: 'password is either too short or not complex enough',
score: 0.9303431734979493
},
{
name: 'topology',
message: 'password matches vulnerable pattern topology'
}
]
}
Plugins
neopass
runs on plugins. Generators and validators are all plugins and can be specified as part of the configuration.
Config:
/** * Configuration */
Generate
console.log'password:', pass
Output:
password: v2mQsx6SKZ3s
Validate
Validate the generated password:
console.log'errors:', errors
Output:
errors: []
Validate some other password:
console.log'errors:', errors
Output:
errors: [
{
name: 'entropy',
msg: 'password is either too short or not complex enough',
score: 0.8078007814753613
},
{
name: 'shannon',
msg: 'password is too simple',
score: 0.8895122952096924
},
{
name: 'sequence',
message: 'password contains at least 2 character sequence(s)'
},
{
name: 'run',
message: 'password contains at least 1 character run(s)',
meta: 1
},
{
name: 'topology',
message: 'password matches vulnerable pattern topology'
}
]
Authoring a Validator Plugin
/** * A simplified version of the LengthValidator plugin */
Configuration:
neo.validate'abc'
Output:
[ { name: 'simple-length',
msg: 'password too short!',
score: 0.3 } ]
Plugins don't have to be subclasses of ValidatorPlugin
or instances of classes at all. Any object that conforms to IPlugin
will do:
/** * Create an object that implements IPlugin to act as * a validator plugin. */ neo.validate'abcdefg'
Output:
[ { name: 'custom-length',
msg: 'password too short!',
score: 0.7 } ]
Password Info
A validator plugin can request any of the following password info items:
password
: the original password as given for validation. Scrutinize any plugin that requests this attribute!
length
: the number of characters in the password.
depth
: the per-character search space of the password, based on which character classes are present.
topology
: the per-character classes of the password, e.g., topology('Abcd1$') => 'ulllds'
. Scrutinize any plugin that requests this attribute!
classes
: a reduction of the topology
; that is, the character classes represented in the password, e.g., classes('Abcdefg') => 'ul'
.
entropy
: the base-2 logarithm of the depth
, as bits per symbol/character.
shannon
: the Shannon Entropy of the password in bits per symbol. Shannon entropy can be thought of as the overall complexity of the password, based on the diversity of symbols it contains.
As a rule, always request the minimum amount of information required to fulfill proper validation. For example:
- if you want to know what the password length is, request
length
and notpassword
ortopology
. - if you want to know which classes are represented, request
classes
and nottopology
.
There are legitimate reasons for a validator to request password
. For instance, both the RunValidator
and the SequenceValidator
use password
to determine if a password has runs of the same character or multiple characters in sequence. Most of the other validators request length
, shannon
, entropy
and/or depth
. The TopologyValidator
requests topology
. However it's dangerous to blindly trust plugins that request password
or topology
. It's safest to only use validators and generators that you author yourself.
Override Configured Chains
The validation and/or evaluation chain defined in the configuration can be overridden by passing a chain definition to validate or evaluate:
// Create a custom validation chain. // Overide default validators.console.log'errors:', errors
Output:
errors: [ { name: 'entropy',
msg: 'password is either too short or not complex enough',
score: 0.7270207033278251 } ]
The above also works with the evaluation chain.