trustelem / zxcvbn

Go implementation of Dropbox's zxcvbn realistic password strength estimator
MIT License
67 stars 13 forks source link

Repeated combining diacritics cause unexpected zero score #12

Open rwelin opened 4 years ago

rwelin commented 4 years ago

When a combining diacritic character is repeated in consecutive runes, the PasswordStrength function gives a score of 0. For example the string below where the \u0300 rune appears twice after the initial A:

s := "A\u0300\u0300HLUMoB8g3kqgpjc3"
result := zxcvbn.PasswordStrength(s, nil)
// result.Score == 0

However, if the repeated combining characters are at the end of the string the score is still high:

s := "HLUMoB8g3kqgpjc3A\u0300\u0300"
result := zxcvbn.PasswordStrength(s, nil)
// result.Score == 4

I've not been able to look into this very deeply but disabling the repeatMatch in Omnimatch results in score of 4.

vanackere commented 3 years ago

Hi,

Our handling of non-ascii passwords is indeed not ideal. The bug might be either in repeatMatch or in the way we compute the scoring... Both parts should probably be rewritten to work on runes (but the rewrite is probably not trivial).

By curiosity: have you tried running this test with the "upstream" dropbox zxcvbn library ? I'm curious about the result.

rwelin commented 3 years ago

Hi! It seems the dropbox zxcvbn node package gives score of 4 for both strings I used in the description:

{
  "requires": true,
  "lockfileVersion": 1,
  "dependencies": {
    "zxcvbn": {
      "version": "4.4.2",
      "resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz",
      "integrity": "sha1-KOwXzwl0PtyrBW3dixsGJizHPDA="
    }
  }
}
> var zxcvbn = require('zxcvbn');
undefined
> zxcvbn('HLUMoB8g3kqgpjc3A\u0300\u0300');
{
  password: 'HLUMoB8g3kqgpjc3À̀',
  guesses: 10000000000000000000,
  guesses_log10: 19,
  sequence: [
    {
      pattern: 'bruteforce',
      token: 'HLUMoB8g3kqgpjc3À̀',
      i: 0,
      j: 18,
      guesses: 10000000000000000000,
      guesses_log10: 19
    }
  ],
  calc_time: 6,
  crack_times_seconds: {
    online_throttling_100_per_hour: 360000000000000000000,
    online_no_throttling_10_per_second: 1000000000000000000,
    offline_slow_hashing_1e4_per_second: 1000000000000000,
    offline_fast_hashing_1e10_per_second: 1000000000
  },
  crack_times_display: {
    online_throttling_100_per_hour: 'centuries',
    online_no_throttling_10_per_second: 'centuries',
    offline_slow_hashing_1e4_per_second: 'centuries',
    offline_fast_hashing_1e10_per_second: '31 years'
  },
  score: 4,
  feedback: { warning: '', suggestions: [] }
}
> zxcvbn('A\u0300\u0300HLUMoB8g3kqgpjc3');
{
  password: 'À̀HLUMoB8g3kqgpjc3',
  guesses: 10000000000000000000,
  guesses_log10: 19,
  sequence: [
    {
      pattern: 'bruteforce',
      token: 'À̀HLUMoB8g3kqgpjc3',
      i: 0,
      j: 18,
      guesses: 10000000000000000000,
      guesses_log10: 19
    }
  ],
  calc_time: 4,
  crack_times_seconds: {
    online_throttling_100_per_hour: 360000000000000000000,
    online_no_throttling_10_per_second: 1000000000000000000,
    offline_slow_hashing_1e4_per_second: 1000000000000000,
    offline_fast_hashing_1e10_per_second: 1000000000
  },
  crack_times_display: {
    online_throttling_100_per_hour: 'centuries',
    online_no_throttling_10_per_second: 'centuries',
    offline_slow_hashing_1e4_per_second: 'centuries',
    offline_fast_hashing_1e10_per_second: '31 years'
  },
  score: 4,
  feedback: { warning: '', suggestions: [] }
}