Giter Site home page Giter Site logo

zxcvbn-ts / zxcvbn Goto Github PK

View Code? Open in Web Editor NEW
732.0 5.0 61.0 25.14 MB

Low-Budget Password Strength Estimation

Home Page: https://www.usenix.org/conference/usenixsecurity16/technical-sessions/presentation/wheeler

License: MIT License

JavaScript 2.05% TypeScript 97.95%
hacktoberfest password password-strength

zxcvbn's Introduction

@zxcvbn-ts/core @zxcvbn-ts/core @zxcvbn-ts/core @zxcvbn-ts/core

zxcvbn-ts

This is a complete rewrite of zxcvbn into typescript which is licensed under the MIT license. Thanks to the original creators dropbox for the great work.

zxcvbn is a password strength estimator inspired by password crackers. Through pattern matching and conservative estimation, it recognizes and weighs 40k common passwords, common names surnames, popular words from Wikipedia and common word in different language from different countries, and other common patterns like dates, repeats (aaa), sequences (abcd), keyboard patterns (qwertyuiop), and l33t speak.

Consider using zxcvbn as an algorithmic alternative to password composition policy — it is more secure, flexible, and usable when sites require a minimal complexity score in place of annoying rules like "passwords must contain three of {lower, upper, numbers, symbols}".

  • More secure: policies often fail both ways, allowing weak passwords (P@ssword1) and disallowing strong passwords.
  • More flexible: zxcvbn allows many password styles to flourish so long as it detects sufficient complexity — passphrases are rated highly given enough uncommon words, keyboard patterns are ranked based on length and number of turns, and capitalization adds more complexity when it's unpredictaBle.
  • More usable: zxcvbn is designed to power simple, rule-free interfaces that give instant feedback. In addition to strength estimation, zxcvbn includes minimal, targeted verbal feedback that can help guide users towards less guessable passwords. For further detail and motivation, please refer to the USENIX Security '16 paper and presentation.

The reason of this project is to modernize zxcvbn and make it maintainable with new features.

Features

  • estimate strength of a password
  • get a score for the password
  • i18n support, for dictionaries and feedback translations
  • extend existing dictionaries with your own
  • usable without dictionaries at all, which reduce the scoring efficiency rapidly. This is not recommended
  • types
  • custom matcher
  • haveibeenpwned matcher

password check example

Documentation

Checkout the Documentation. There you will also find the Demo pages and the Migration guide.

LanguagePackages

If your language is missing as a language pack checkout the guide to add your own.

Comparison

If you want to know how much the scoring changed compared to the original checkout the comparison page.

Contribution

Please feel free to open up an issue or provide a pull request.

zxcvbn's People

Contributors

allanlewis avatar antoson avatar aybabtme avatar bgwastu avatar codebycaleb avatar codetheweb avatar domosapien avatar dwolfhub avatar gabberr avatar gerarts avatar hrueger avatar lowe avatar marazmarci avatar mrwook avatar nathanreb avatar nmalkin avatar oskar-gmerek avatar raphaelmp avatar rasky avatar reedloden avatar remcohaszing avatar rianhunter avatar robertoyoc avatar rohitjha avatar sandro-pasquali avatar studds avatar sudo-plz avatar tomsommer avatar zeezooz avatar zlatanvasovic avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

zxcvbn's Issues

How to use AZERTY in 0.3.0?

In the changelog it is noted that AZERTY support is added in 0.3.0, but this is not yet reflected in the documentation (which is for 1.0.0 beta, I think). How do you enable it?

In the documentation:

By default zxcvbn-ts don't use any keyboard patterns to let the developer decided how much of the library will be used. It is recommended to use at least the common keyboard patterns.

But there is no documentation on how to enable this (and more specifically to enable only AZERTY). And there are no adjacencyGraphs available in 0.3.0:

Type definition of @zxcvbn-ts/language-common:

declare const _default: {
    dictionary: {
        userInputs: string[];
        passwords: string[];
    };
    // <- no adjacencyGraphs as stated in the documentation
};
export default _default;

Resolve Internal function references

There are a few old functions internally that are using references. Which means there a function that receive an object as an argument and change this object directly. This is unintuitive and should be changed.

Change return value of feedback.warning when there is no warning given

Currently returns the following if no warning found: -

feedback: { warning: '', suggestions: [] }

Suggest changing type of warning to string | null or string[]

Reason - empty string still indicates on a programmatic level that a warning "exists". Even though the warning is not currently an array (rather a single string), a return of warning: [] or warning: ["A warning message here"] may be a logical option.

If a string (rather than an array of strings) is to be returned, suggest warning: null or warning: "A warning message here" to establish non-existence of warning. This will allow for if(feedback.warning) {...} rather than if(feedback.warning.length) {...}, which is somewhat hackish.

Export more types index.d.ts

Thanks for the great work! I just switched over from the original dropbox-"zxcvbn" and I'm very happy.
One thing that made my implementation a bit cumbersome is the fact that most types are not exported. That makes customisation harder that it needs to be.

For example: I process the result of the function to generate a more detailed explanation to the user. It is easy to do that with the return type of the zxcvbn function. For that I have a function that accepts a MatchExtended and generates a message. But the MatchExtended type cannot be imported, so I had to redeclare the type in my own code.

Just exporting some more types in index.d.ts would solve this.
I can create a PR for this myself if that is appreciated.

matcher-pwned requests

Hey,
when using the pwnd-matcher there is a request for every repeat match.
Is that intentional?
Is there a way to prevent that behavior, or is it a bad idea?

Remove userInputs from language dictionaries

Discussed in #68

Originally posted by paulverbeke September 18, 2021
Hi @MrWook hope you're doing well.
I finally got the time to play around with this library.
I'm trying to implement it in one of my project but I cannot make the userInputs dictionnary working for me.
I'm setting these options:
const options3 = { translations: zxcvbnts['language-fr'].translations, graphs: zxcvbnts['language-common'].adjacencyGraphs, dictionary: { userInputs: ['[email protected]'], ...zxcvbnts['language-common'].dictionary, ...zxcvbnts['language-en'].dictionary, ...zxcvbnts['language-fr'].dictionary }, } zxcvbnts.core.ZxcvbnOptions.setOptions(options3)

calling with this password:
zxcvbnts.core.zxcvbn("[email protected]")

and getting this result with a score of 4 and no suggestions -_-:
calcTime: 13 crackTimesDisplay: Object { onlineThrottling100PerHour: "siècles", onlineNoThrottling10PerSecond: "siècles", offlineSlowHashing1e4PerSecond: "siècles", … } crackTimesSeconds: Object { onlineThrottling100PerHour: 271252803600000000, onlineNoThrottling10PerSecond: 753480010000000, offlineSlowHashing1e4PerSecond: 753480010000, … } feedback: Object { warning: "", suggestions: [] } guesses: 7534800100000000 guessesLog10: 15.877071734869821 password: "[email protected]" score: 4

What am I doing wrong here ?

Basically the language dictionaries have the userInputs as a default empty array. If you add your userInputs dictionary before you import one of the other dictionaries your userInputs dictionary will be overwritten.
This is pretty confusing which means the userInput needs to be removed from the language dictionaries.

Add option to configure networkError handling

A mention in #129 we should add an option to configure how the network errors are handled for async matchers

Possibly a configuration function handleNetworkError: (error:Err) => boolean | Error (where the default is false)

There are different use cases for zxcvbn. While the UX/UI use cases expects a flawless execution of zxcvbn, a backend service might want to know if there was an error

Dynamic ECMA2020 import not working client-side, only server-side

In Meteor, I've been dynamically importing and using zxcvbn and it works great.
import('/node_modules/zxcvbn/dist/zxcvbn.js').then( ({default:zxcvbn}) => {

Meteor offers ECMA2020 style dynamic imports.
https://docs.meteor.com/packages/dynamic-import.html

However when I try to dynamically import zxcvbn-ts, the import comes through on the server-side during the React SSR (server-side render). But the result of the import() promise is empty on the clientside :(
import('/node_modules/@zxcvbn-ts/core/dist/zxcvbn-ts.js').then( foo => { console.log("foo", foo)
Foo shows an empty import: { default: {}, Symbol("__esModule"): true, Symbol(Symbol.toStringTag): "Module" }

And simultaneously the same dynamic import code works fine on the server-side during SSR

foo [Object: null prototype] [Module] {
  default: { zxcvbnts: { core: [Object] } },
  zxcvbnts: { core: { ZxcvbnOptions: [Options], zxcvbn: [Function: zxcvbn] } },
  [Symbol(__esModule)]: true
}

So, on the server side, I can do this without issues:
import('/node_modules/@zxcvbn-ts/core/dist/zxcvbn-ts.js').then( ({zxcvbnts:{core:{zxcvbn, ZxcvbnOptions}}}) => {

I had a similar problem to this in another package, not sure if seeing the issue and solution there would be helpful
center-key/pretty-print-json#41

I wondered if perhaps your @ sign in the module name is causing a problem. But if I put in \\@ in the path then I get an error saying module not found. So the import code above definitely "finds" the module, whatever that means. But the import comes through empty on the clientside.

I'm importing the stuff separately like this.

import('/node_modules/@zxcvbn-ts/core/dist/zxcvbn-ts.js').then( ({zxcvbnts:{core:{zxcvbn, ZxcvbnOptions}}}) => { ...

import('/node_modules/@zxcvbn-ts/language-common/dist/zxcvbn-ts.js').then( ({default:zxcvbnCommonPackage}) => { ...

import('/node_modules/@zxcvbn-ts/language-en/dist/zxcvbn-ts.js').then( ({default:zxcvbnEnPackage}) => { ...

Then updating state when all the imports have come through, etc.

Add Levenshtein Distance for dictionaries

It would be nice if the dictionary matching has the ability to use Levenshtein Distance (LD) calculations to match passwords which are non-exact matches to a dictionary entry.
For example that something like this would match:
{someDictionary: ['Herbert', 'Dorothea']} with the "password" Hebert would match the first entry of the dictionary.
The downside is that this would decrease the performance of the library.

Improving userInputs dictionnary feedback

Hi,
First of all thanks a lot for this work. The original Dropbox library desperately needed maintenance.

I saw that french translations were recently added by someone, in looking into this I noticed that, no matter the language, the only feedback given when password contains an entry from the userInputs dictionary is 'There should not be any personal data.'.
Based on the original library documentation, this dictionary should also be used for site-specific vocabulary, so can I suggest that this feedback be changed to something more representative of what the dev is likely to have added, in the line of "There should not be any personal data or forbidden/banned words", or just a more general feedback ?

I can take care of updating french translations if needed.

Thanks !

Originally posted by @paulverbeke in #36 (comment)

Refactor keyboard patterns

Currently we got a bunch of keyboard patterns it would be nice if those keyboard patterns are optional like the dictionaries.
This way we could make a common set and add language specific keyboard patterns like JIS or JCUKEN without polluting everyone else.
Also this would mean we could lazy load the keyboard patterns which are currently 15kb minified and decrease the main bundle size by the same.

Pwned matcher not working in web workers

I'm using the library inside a web worker without any issues.
When trying to add the @zxcvbn-ts/matcher-pwned matcher, an error is thrown due to window not being available in the worker context.

The following line causes the issue:

if (window.TextEncoder) {
return new TextEncoder().encode(text)
}

TextEncoder is available in workers however.
So changing lines 15-18 to something like this should solve the problem:

try {
  return new TextEncoder().encode(text)
} catch (err) {
  throw new Error('No encoder found', { cause: err })
}

I'd be happy to provide a pull request if desired.
Thank you!

Road to 1.0.0

At some point this project should have a full release for 1.0.0.
Therefore i places a milestone on some issues and PR that are needed to be included to minimise the Breaking changes after the release.

  • internal types should be fixed => types fixed
  • Have i Been Pwned API Support => will result in a the api change because this is async
  • add some feedback for userinput dictionaries => nice to have
  • The documentation need some spelling, grammar improvements
  • Maybe add custom matcher => would add a new option
  • add async matcher support

Pre-processing in the generator to filter out compound names.

For Spanish the generators work correctly but we should add some pre-processing function to filter compound names.

Spanish (ab)uses compound first names. So instead of being named "Antonio", "Jose", "Pedro" your parents choose to call you "Antonio Manuel", "Jose Miguel" or "Pedro Antonio" to honour(keep happy) other family members.

And as this is quite popular the list of first names given by the government is plagued by such. For the purpose of the matcher this is useless as any of the compound names will already match the single name from the list.

I'm sending a new PR for the Spanish language pack including compound names and I open a new discussion on how to deal with compound names.

Originally posted by @JonL1 in #82 (comment)

DATE_SPLITS skip possible case

Looking comments in original code
https://github.com/dropbox/zxcvbn/blob/67c4ece9efc40c9d0a1d7d995b2b22a91be500c2/src/matching.coffee#L42-L65
I think the branch for 5 skip 1 case and should be the "same" as 7 branch:

DATE_SPLITS =
  4:[      # for length-4 strings, eg 1191 or 9111, two ways to split:
    [1, 2] # 1 1 91 (2nd split starts at index 1, 3rd at index 2)
    [2, 3] # 91 1 1
    ]
  5:[
    [1, 3] # 1 11 91
    [2, 3] # 11 1 91
 #  [2, 3] # 91 1 11    <- New but duplicate previous one and must be removed
    [2, 4] # 91 11 1    <- New and must be added as bug fix
    ]
  6:[
    [1, 2] # 1 1 1991
    [2, 4] # 11 11 91
    [4, 5] # 1991 1 1
    ]
  7:[
    [1, 3] # 1 11 1991
    [2, 3] # 11 1 1991
    [4, 5] # 1991 1 11
    [4, 6] # 1991 11 1
    ]
  8:[
    [2, 4] # 11 11 1991
    [4, 6] # 1991 11 11
    ]

After the change I'd expect resulting passwords.json will be a bit short :-). This could be a check for the change.

Inconsistent user inputs affect on score?

I may well be missing something here, but this example appears to be very insecure: -

const userInputs = ["Brian Thompson"]

I've ran the following tests and there are the result: -

TEST 1

Password: "Brian Thompson"

zxcvbnResult :  {
  calcTime: 138,
  password: 'Brian Thompson',
  guesses: 456,
  guessesLog10: 2.658964842664435,
  sequence: [
    {
      pattern: 'dictionary',
      i: 0,
      j: 13,
      token: 'Brian Thompson',
      matchedWord: 'brian thompson',
      rank: 5,
      dictionaryName: 'userInputs',
      reversed: false,
      l33t: false,
      baseGuesses: 5,
      uppercaseVariations: 91,
      l33tVariations: 1,
      guesses: 455,
      guessesLog10: 2.658011396657112
    }
  ],
  crackTimesSeconds: {
    onlineThrottling100PerHour: 16416,
    onlineNoThrottling10PerSecond: 45.6,
    offlineSlowHashing1e4PerSecond: 0.0456,
    offlineFastHashing1e10PerSecond: 4.56e-8
  },
  crackTimesDisplay: {
    onlineThrottling100PerHour: '5 hours',
    onlineNoThrottling10PerSecond: '46 seconds',
    offlineSlowHashing1e4PerSecond: 'less than a second',
    offlineFastHashing1e10PerSecond: 'less than a second'
  },
  score: 0,
  feedback: {
    warning: 'There should not be any personal or page related data.',
    suggestions: [ 'Add more words that are less common.' ]
  }
}

TEST 2

Password: "Brian Thompson " (note the space at the end)

zxcvbnResult :  {
  calcTime: 138,
  password: 'Brian Thompson ',
  guesses: 20010,
  guessesLog10: 4.30124708863621,
  sequence: [
    {
      pattern: 'dictionary',
      i: 0,
      j: 13,
      token: 'Brian Thompson',
      matchedWord: 'brian thompson',
      rank: 5,
      dictionaryName: 'userInputs',
      reversed: false,
      l33t: false,
      baseGuesses: 5,
      uppercaseVariations: 91,
      l33tVariations: 1,
      guesses: 455,
      guessesLog10: 2.658011396657112
    },
    {
      pattern: 'bruteforce',
      token: ' ',
      i: 14,
      j: 14,
      guesses: 11,
      guessesLog10: 1.041392685158225
    }
  ],
  crackTimesSeconds: {
    onlineThrottling100PerHour: 720360,
    onlineNoThrottling10PerSecond: 2001,
    offlineSlowHashing1e4PerSecond: 2.001,
    offlineFastHashing1e10PerSecond: 0.000002001
  },
  crackTimesDisplay: {
    onlineThrottling100PerHour: '8 days',
    onlineNoThrottling10PerSecond: '33 minutes',
    offlineSlowHashing1e4PerSecond: '2 seconds',
    offlineFastHashing1e10PerSecond: 'less than a second'
  },
  score: 1,
  feedback: {
    warning: 'There should not be any personal or page related data.',
    suggestions: [ 'Add more words that are less common.' ]
  }
}

TEST 3

Password: "Brian Thompson password"

zxcvbnResult :  {
  calcTime: 364,
  password: 'Brian Thompson password',
  guesses: 101501500,
  guessesLog10: 8.00647246034686,
  sequence: [
    {
      pattern: 'dictionary',
      i: 0,
      j: 13,
      token: 'Brian Thompson',
      matchedWord: 'brian thompson',
      rank: 5,
      dictionaryName: 'userInputs',
      reversed: false,
      l33t: false,
      baseGuesses: 5,
      uppercaseVariations: 91,
      l33tVariations: 1,
      guesses: 455,
      guessesLog10: 2.658011396657112
    },
    {
      pattern: 'bruteforce',
      token: ' ',
      i: 14,
      j: 14,
      guesses: 11,
      guessesLog10: 1.041392685158225
    },
    {
      pattern: 'dictionary',
      i: 15,
      j: 22,
      token: 'password',
      matchedWord: 'password',
      rank: 2,
      dictionaryName: 'passwords',
      reversed: false,
      l33t: false,
      baseGuesses: 2,
      uppercaseVariations: 1,
      l33tVariations: 1,
      guesses: 50,
      guessesLog10: 1.6989700043360185
    }
  ],
  crackTimesSeconds: {
    onlineThrottling100PerHour: 3654054000,
    onlineNoThrottling10PerSecond: 10150150,
    offlineSlowHashing1e4PerSecond: 10150.15,
    offlineFastHashing1e10PerSecond: 0.01015015
  },
  crackTimesDisplay: {
    onlineThrottling100PerHour: 'centuries',
    onlineNoThrottling10PerSecond: '4 months',
    offlineSlowHashing1e4PerSecond: '3 hours',
    offlineFastHashing1e10PerSecond: 'less than a second'
  },
  score: 3,
  feedback: { warning: '', suggestions: [] }
}

TEST 4

Password: "Brian Thompson 1234"

zxcvbnResult :  {
  calcTime: 203,
  password: 'Brian Thompson 1234',
  guesses: 91010000,
  guessesLog10: 7.959089114367391,
  sequence: [
    {
      pattern: 'dictionary',
      i: 0,
      j: 13,
      token: 'Brian Thompson',
      matchedWord: 'brian thompson',
      rank: 5,
      dictionaryName: 'userInputs',
      reversed: false,
      l33t: false,
      baseGuesses: 5,
      uppercaseVariations: 91,
      l33tVariations: 1,
      guesses: 455,
      guessesLog10: 2.658011396657112
    },
    {
      pattern: 'bruteforce',
      token: ' 1234',
      i: 14,
      j: 18,
      guesses: 100000,
      guessesLog10: 5
    }
  ],
  crackTimesSeconds: {
    onlineThrottling100PerHour: 3276360000,
    onlineNoThrottling10PerSecond: 9101000,
    offlineSlowHashing1e4PerSecond: 9101,
    offlineFastHashing1e10PerSecond: 0.009101
  },
  crackTimesDisplay: {
    onlineThrottling100PerHour: 'centuries',
    onlineNoThrottling10PerSecond: '3 months',
    offlineSlowHashing1e4PerSecond: '3 hours',
    offlineFastHashing1e10PerSecond: 'less than a second'
  },
  score: 2,
  feedback: {
    warning: 'There should not be any personal or page related data.',
    suggestions: [ 'Add more words that are less common.' ]
  }
}

In test 1, it rightly gives a score of zero and a warning for "Brian Thompson" as "Brian Thompson" is a user input.

In test 2, a single trailing white space will give a score other than 0 and a warning.

In test 3, "Brian Thompson password" will give a score of 3 and no warnings/suggestions, despite it seems to be what should be considered a low (or lower) entropy password assuming the attacker has the user's name. This would seem to be an error that should be addressed as I think it creates opportunity for weak passwords.

In test 4, a password of "Brian Thompson 1234" - which I think we should consider to have a similar entropy/obviousness to that of test 3 - returns a score of 2 with warnings/suggestions. Is this not inconsistent with test 3's feedback?

Also, should there not just be a case for the failing of a password if it has "Brian Thompson" or a l33t of "Brian Thompson" anywhere in the password string? Should we not consider user input + " password" to be rather obvious?

The user can write their own function to check the password for substrings of user inputs but this is 1) hackish 2) difficult to take into account l33t as l33t would have to be done by a separate package (unless the zxcvbn-ts l33t function can be made available for direct import).

Thoughts?

Thank you

Hey 👋🏽

Thank you for the work at this.

I don't know typescript / JavaScript, but can I buy you a coffee or too?

Maxi

Add extremely common word sequences?

TLDR:
123456 is pretty much the most common password in the world and also has no entropy due to being an obvious sequence.
zxcvbn-ts falls on it's face with onetwothreefourfivesix, rating it as maximum strength.
Let's fix that?


Just an idea, not sure if this is commonly done with passwords.
But just like 123456789 or 987654321 or abcdefg, etc is seen as completely lacking entropy... what about

Months
januaryfebruarymarch
julyjunemay

Written numbers
onetwothree
nineeightseven

Seasons
springsummerautumn
winterspringsummer

Bible chapters
genesisexoduswhatever etc

Sizes
smallmediumlarge
largemediumsmall

Greek whatever
alphabeta etc

Phonetic alphabet
alphabravocharliedelta
tangosierraromeo

zxcvbn-ts currently thinks all this sort of junk is a strong password (might need to add an extra word in some cases, but normally 3-4 words, and it thinks you're golden), when you've basically got no entropy if you're using any of the above.

Obviously there's an endless amount of common sequences people could put into a password.
Like listing the characters of a popular tv series.

But I figured the categories I wrote above should be standard, because regardless of a person's preferences or personality, they'll deal with (or be familiar with) most, if not all of the above. With the exception of maybe awareness of the bible chapter names.

Bruteforce matcher seems to aggresive

For some reason the bruteforce matcher has a different outcome than the original library for example zxcftzuio
will result in the original to 'zxcft' for spatial and 'zuio' for bruteforce while this library only has zxcftzuio for bruteforce..
In this example this will only result in a slight change of the guesses_log10 and not on the score but this should not be an issue in the first place.
It need to be checked what results in the different word usages.

TypeError: password.toLowerCase is not a function

Hi!

I am just trying this amazing library but unfortunately after almost copying / pasting the demo from the docs I get an error.

Unhandled Runtime Error
TypeError: password.toLowerCase is not a function

From this line:
zxcvbn(password)

related line in zxcvbn:
const passwordLower = password.toLowerCase(); // eslint-disable-next-line complexity

Im using:

    "next": "^12.1.4",
    "react": "^17",
    "react-dom": "^17",
    "@zxcvbn-ts/core": "^2.0.1",
    "@zxcvbn-ts/language-common": "^2.0.1",
    "@zxcvbn-ts/language-en": 
    "typescript": "latest"

Do I do anything wrong or it is not compatible with something used?

A brilliant - and also terrible - idea - for discussion only

Why it's brilliant
Search the first n characters of the password on a search engine.
See how many results there are.
That can be like as one of the password scores. (the more results, lower the score)

Why it's terrible
The password will no longer be a secret if you send it to a search engine.
This is probably a showstopper for the idea.
You can limit the damage by only sending half of the password, or the first n words/tokens or whatever.

Inspired by the discussion in #63
The reason I came up with the idea. Is if someone uses a password like
maryhadalittlelambwhosfleecewaswhiteassnow
or whatever, it would likely return a billion search results, even though zxcvbn as is would probably think it's an amazing password.

Usage with import maps

How would one use this library through import maps?

I tried to add it to my importmap, as follows

{ "imports": { "zxcvbn-ts": "/js/zxcvbn-ts.js" } }

Then, in another module:

import { zxcvbn, ZxcvbnOptions } from 'zxcvbn-ts'

The zxcvbn-ts.js file itself is correctly fetched by the browser, but since the code is wrapped inside an anonymous function which does not get executed (because it's fetched as a module), then the exports are not defined.

I am not very familiar with all the bundling/import methods of Javascript modules, but is there a way to use zxcvbn-ts with import maps? Or am I missing something?

I do not want to use any bundler, I only want to use ES6 modules with import maps.

Update Example Documentation

The documentation shows using the script tag as a way to implement zxcvbn. The version numbers are out of date. As well the language-common link has an invalid version number.
image

https://zxcvbn-ts.github.io/zxcvbn/guide/getting-started/#installation

In addition the code in the example does not work. Using 0.3.0 I was able to get it to run with

(function () {
    // all package will be available under zxcvbnts
    const options = {
        translations: zxcvbnts['language-en'].translations,
        graphs: zxcvbnts['language-common'].adjacencyGraphs,
        dictionary: {
            ...zxcvbnts['language-common'].dictionary,
            ...zxcvbnts['language-en'].dictionary,
        },
    }
    zxcvbnts.core.ZxcvbnOptions.setOptions(options)
    console.log(zxcvbnts.core.zxcvbn('AD@gSatOn1Cat!'))
})()

Strictly type result>score to 0, 1, 2, 3, 4 and make exportable

As per documentation "result.score # Integer from 0-4 (useful for implementing a strength bar)"

Current type is just number: -

export interface ZxcvbnResult {
  feedback: FeedbackType
  crackTimesSeconds: CrackTimesSeconds
  crackTimesDisplay: CrackTimesDisplay
  score: number
  password: string
  guesses: number
  guessesLog10: number
  sequence: MatchExtended[]
  calcTime: number
}

Should be: -

export type Score = 0 | 1 | 2 | 3 | 4

Stricter typing and export can be used for such cases as a React component state which feeds into a password strength bar.

Feature Request: Lazy Dictionary Loading

zxcvbn is great but the dictionary is huge! When I build my application it would be nice if there was a way to load zxcvbn using a promise, in which it will acquire the dictionary from a CDN, parse and instantiate. This will help reduce application payload sizes for SPA use cases.

Diceware testing

It seems diceware passwords are often considered good with zxcvbn (I really like both zxcvbn and diceware). However as pointed out on this security exchange answer technically diceware without user modification have a dictionary size of 8000 so the randomness for 8000 words is 8000^3= 512 billion possible phrases. So even though the passphrase maybe 'TurkeyGlanceUnbiased' gets a good zxcvbn rating, its basically the same as 'abc'.

Should zxcvbn measure against known diceware dictionaries?

Performance issues

I've set up an app with zxcvbn-ts to show password strength and offer suggestions to the user. Nothing special, the options are exactly as show in the docs. Also want to say zxcvbn is a great project and I appreciate the work you're doing to keep it going.

I've noticed, though, that zxcvbn-ts is not fast enough to run on each keypress (value update in React). When rendering the password strength component with zxcvbn-ts, there is noticeable lag while typing (especially when typing as fast as possible as a stress test). The base zxcvbn package, on the other hand, is able to calculate password strength on keypress without any noticeable delay.

Is there any way to improve performance when using this package?

I've seen this line in the README: "usable without dictionaries at all, which reduce the scoring efficiency rapidly." (I'm assuming it means reduce the scoring time or increase efficiency), however it's followed by "This is not recommended."

Happy to help out with this project too if you can point me in the direction of areas that might be affecting performance.

For reference here are my options:

const zxcvbnOptions = {
    dictionary: {
        ...zxcvbnCommonPackage.dictionary,
        ...zxcvbnEnPackage.dictionary,
    },
    translations: zxcvbnEnPackage.translations,
};

Sequence in ZxcvbnResult should be MatchEstimated[]

The sequence property in the type for ZxcvbnResult is of type MatchExtended[]. However, I believe this can be MatchEstimated[], which is MatchExtended & Estimate. The necessary properties seem to be available on all matches at least.

Enable adding user inputs to the dictionary on-the-fly through core function args

The original library allows passing in user inputs to the actual core function (the one you would call with a password). This port instead allows passing said inputs through the SetOptions interface, which is a valid design decision.

It would be convenient to also allow the old way, for the following reason: Creating the Options argument may involve costly work in some workflows - including dynamically importing the language packs, and creating the actual dictionary, for example.

Providing this additional interface can allow us to decouple the work needed in order create the initial options object from the much lighter work of handling constantly changing user input.

I can take a look at the code later and maybe submit a pull request if you think this is too much trouble.

Custom matcher

There was some issues and requests to have more simple matchers like password min length.
In my opinion this is nothing this library should handle because zxcvbn is intended to get rid of the old paradigm "passwords must contain three of {lower, upper, numbers, symbols}"

On the other hand i know people will still add those checks in addition to zxcvbn. Therefore we could simplify things and add an option to add custom matchers.

Quality of score / suggestions / warnings

Hi,

I was testing my implementation and found out something strange.

Password: johnwick1233
Score: 4 / 4
guess times (10 / second): centuries
match sequence: [passwords, lastname, date]
Suggestions: no suggestions
Warning: no warnings
Same password reduced by last character is pwned as well (johnwick123)

Password: johnwick2022
Score: 3 / 4
guess times (10 / second): 1 year
match sequence: [passwords, lastname, recentYear]
Suggestions: no suggestions
Warning: no warnings

Password: johnwick2030
Score: 4 / 4
guess times (10 / second): centuries
match sequence: [passwords, bruteforce]
Suggestions: no suggestions
Warning: no warnings

Password: fuckputin2022
Score: 3 / 4
guess times (10 / second): 10 months
match sequence: [passwords, wikipedia, recentYear]
Suggestions: no suggestions
Warning: no warnings

Password: johndoe1233
Score: 3 / 4
guess times (10 / second): 4 months
match sequence: [passwords, date]
Suggestions: no suggestions
Warning: no warnings

Password: sunnyflower12
Score: 3 / 4
guess times (10 / second): 4 months
match sequence: [passwords, passwords]
Suggestions: no suggestions
Warning: no warnings

Password: sunnyflower12
Score: 3 / 4
guess times (10 / second): 4 months
match sequence: [passwords, passwords]
Suggestions: no suggestions
Warning: no warnings

Password: sun.flower123
Score: 3 / 4
guess times (10 / second): 2 years
match sequence: [bruteforce, passwords, sequence]
Suggestions: no suggestions
Warning: no warnings

Password: [email protected]
Score: 4 / 4
guess times (10 / second): centuries
match sequence: [bruteforce, passwords]
Suggestions: no suggestions
Warning: no warnings

Password: john@netflix
Score: 3 / 4
guess times (10 / second): 4 months
match sequence: [firstnames, wikipedia]
Suggestions: no suggestions
Warning: no warnings

Password: example@gmail
Score: 3 / 4
guess times (10 / second): 1 year
match sequence: [bruteforce, wikipedia, commonWords]
Suggestions: no suggestions
Warning: no warnings

Password: mark&olivia
Score: 3 / 4
guess times (10 / second): 5 months
match sequence: [passwords, bruteforce, passwords]
Suggestions: no suggestions
Warning: no warnings

I think all of above passwords are weak, if not very weak.
I think the score is much too high for that kind of passwords.
I think that warnings and/or suggestions should show up for all of them.

The most important here is scoring, score 4/4 suggesting that password is at least great.
"johnwick2030" got 4/4, but max score should get password like "f6376c03-4#0D78-11c7@35d94-79a3df2#314Fd63@s73GgM!!"
I guessing that most of developers using zxcvbn-ts in their software are using scoring to give feedback to users how strong or weak are they passwords. If they give the same feedback for both above passwords, that is nearly clear that user will think that passwords are both great. But they are not strong on the same level, not even close for first one to second one.

Im thinking that score scale should be 0/10. All of the above password should left at 3/10 or 4/10.
Additional password score should be based on length, number of special characters, number of numbers, number of sequences (and avg length of sequences).
I think that scoring 7/10 and above should be impossible for passwords which can be remembered by human. That will show to users that they no need a great password to be secure, but if they use a best password managers to generate 10/10 password for them, they can be sure that they do everything what they can do if is about of creating passwords.

I guessing that changing scale of score, and adding additional points for passwords based on their length, used special characters, numbers etc should be not that hard to do (but I just guessing) and after that library will benefit a lot.

@MrWook What do you think? Is that even possible?

Async matcher does not handle rejections

Rejections thrown by async matchers are not handled correctly. They end up as Uncaught (in Promise) type errors

To reproduce this:

  • go to https://zxcvbn-ts.github.io/zxcvbn/demo/
  • In the chrome developer toolbar, disable your internet connection
  • Type something in the password field
  • You'll notice the uncaught errors popping up in the developer toolbar

err

Not having a proper mechanism for error handling means that we cannot use things like the pnwd matcher in production

Improve overall code quality

  • There are still a lot of ts-ignore comments
  • we should activate strict type checking
  • some functions are still using references. This is hard to follow so it should be separated
  • maybe use some stricter linting rules

Enable type narrowing on core function result

The current implementation of the core function is:

export const zxcvbn = (
  password: string,
): ZxcvbnResult | Promise<ZxcvbnResult> => {
  const matching = new Matching()

  const start = time()

  const matches = matching.match(password)

  if (matches instanceof Promise) {
    return matches.then((resolvedMatches) => {
      return createReturnValue(resolvedMatches, password, start)
    })
  }
  return createReturnValue(matches, password, start)
}

Summary

The typing of the return type is a union: Either a result, or a promise wrapping a result. Narrowing this using generics for example may improve usage cleanliness.

Explanation

This union is necessary because matchers may be asynchronous:

const usedMatcher = new Matcher()
const result = usedMatcher.match({
  password,
  omniMatch: this,
})

if (result instanceof Promise) {
  result.then((response) => {
    extend(matches, response)
  })
  promises.push(result)
} else {
  extend(matches, result)
}

This is all well and good, but currently at compile time we know that all of the builtin matchers are synchronous.

  readonly matchers: Matchers = {
    date: dateMatcher,
    dictionary: dictionaryMatcher,
    regex: regexMatcher,
    repeat: repeatMatcher,
    sequence: sequenceMatcher,
    spatial: spatialMatcher,
  }

So as of now, only user submitted matchers can be asynchronous. I'd guess that most of the libraries users don't create custom matchers, and even when they do they wouldn't necessarily make them async. So forcing all users of the library to deal with a promise-wrapped value is unfortunate (It is possible that new builtin async matchers are planned in the short term future - that would make my point irrelevant).

Allowing type narrowing using generics, say, where a generic argument may be the type of the matchers passed to the options interface, may be a simple solution that doesn't require recoding the entire typings of the library.

NOTE
It should also be added that the asynchronous nature of the function is currently not mentioned anywhere in the docs, so if the state of the function should remain the same an update of the docs is in order.

If this seems like alot of work to the maintainer(s) of the library, I could look into submitting a pull request.

JavaScript heap out of memory

Importing the common and en language packages using webpack (under CRA4) causes the dev server memory usage to skyrocket upon start, resulting in an immediate V8 out-of-memory crash.

Usage:

import zxcvbnCommonPackage from '@zxcvbn-ts/language-common/'
import zxcvbnEnPackage from '@zxcvbn-ts/language-en/dist/'

const password = 'somePassword'
const options = {
  translations: zxcvbnEnPackage.translations,
  graphs: zxcvbnCommonPackage.adjacencyGraphs,
  dictionary: {
    ...zxcvbnCommonPackage.dictionary,
    ...zxcvbnEnPackage.dictionary,
  },
}

ZxcvbnOptions.setOptions(options)

zxcvbn(password)

Stack trace:

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
 1: 00007FF60F0C412F napi_wrap+133311
 2: 00007FF60F05DD06 SSL_get_quiet_shutdown+63062
 3: 00007FF60F05EB9D node::OnFatalError+301
 4: 00007FF60F9419CE v8::Isolate::ReportExternalAllocationLimitReached+94     
 5: 00007FF60F9267BD v8::SharedArrayBuffer::Externalize+781
 6: 00007FF60F7CFFCC v8::internal::Heap::EphemeronKeyWriteBarrierFromCode+1516
 7: 00007FF60F7DB3EA v8::internal::Heap::ProtectUnprotectedMemoryChunks+1258  
 8: 00007FF60F7D8529 v8::internal::Heap::PageFlagsAreConsistent+2457
 9: 00007FF60F7CD0C1 v8::internal::Heap::CollectGarbage+2049
10: 00007FF60F7CB2C5 v8::internal::Heap::AllocateExternalBackingStore+1349    
11: 00007FF60F7EB73B v8::internal::Factory::NewFillerObject+203
12: 00007FF60F519E0F v8::internal::interpreter::JumpTableTargetOffsets::iterator::operator=+1039
13: 00007FF60F9CCDFD v8::internal::SetupIsolateDelegate::SetupHeap+474253
14: 0000011BD541E117
npm ERR! code ELIFECYCLE
npm ERR! errno 134
npm ERR! [email protected] start: `react-scripts start`
npm ERR! Exit status 134
npm ERR! 
npm ERR! Failed at the [email protected] start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.```

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.