Skip to content

Commit f54add5

Browse files
committed
add max password length, default 72, configurable via ZXCVBN_MAX_LENGTH env var
1 parent f596bef commit f54add5

File tree

4 files changed

+37
-1
lines changed

4 files changed

+37
-1
lines changed

README.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,18 @@ Output:
103103
}],
104104
}
105105

106+
To override the default maximum password length of 72 characters, set the
107+
``ZXCVBN_MAX_LENGTH`` environment variable:
108+
109+
.. code-block:: bash
110+
111+
export ZXCVBN_MAX_LENGTH=128
112+
113+
.. warning::
114+
We strongly advise against setting ``ZXCVBN_MAX_LENGTH`` to a value greater than 72,
115+
as it can lead to long processing times and may leave server-side applications open
116+
to denial-of-service scenarios.
117+
106118

107119
Custom Ranked Dictionaries
108120
--------------------------
@@ -121,6 +133,7 @@ In order to support more languages or just add password dictionaries of your own
121133
These lists will be added to the current ones, but you can also overwrite the current ones if you wish.
122134
The lists you add should be in order of how common the word is used with the most common words appearing first.
123135

136+
124137
CLI
125138
~~~
126139

tests/l33t_exploit_test.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import pytest
2+
from zxcvbn import zxcvbn
3+
4+
# Test ACsploit-generated password targeting zxcvbn's l33t matching algorithm
5+
# (see https://github.com/GoSimpleLLC/nbvcxz/issues/60)
6+
def test_l33t_exploit():
7+
8+
password = "4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/4@8({[</369&#!1/|0$5+7%2/"
9+
10+
# Function should raise ValueError for input exceeding default MAX_LENGTH of 72 chars
11+
with pytest.raises(ValueError, match="Password length exceeds 72 characters"):
12+
zxcvbn(password, user_inputs=[None])

tests/zxcvbn_test.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# -*- coding: utf-8 -*-
2+
import pytest
23
from zxcvbn import zxcvbn
34

45

@@ -23,7 +24,9 @@ def test_long_password():
2324
input_ = None
2425
password = "weopiopdsjmkldjvoisdjfioejiojweopiopdsjmkldjvoisdjfioejiojweopiopdsjmkldjvoisdjfioejiojweopiopdsjmkldjvoisdjfioejiojweopiopdsjmkldjvoisdjfioejiojweopiopdsjmkldjvoisdjfioejiojweopiopdsjmkldjvoisdjfioejiojweopiopdsjmkldjvoisdjfioejiojweopiopdsjmkldjvoisdjfioejiojweopiopdsjmkldjvoisdjfioejiojweopiopdsjmkldjvoisdjfioej"
2526

26-
zxcvbn(password, user_inputs=[input_])
27+
# Function should raise ValueError for input exceeding default MAX_LENGTH of 72 chars
28+
with pytest.raises(ValueError, match="Password length exceeds 72 characters"):
29+
zxcvbn(password, user_inputs=[input_])
2730

2831

2932
def test_dictionary_password():

zxcvbn/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1+
import os
12
from datetime import datetime
23

34
from . import matching, scoring, time_estimates, feedback
45

6+
DEFAULT_MAX_LENGTH = 72
7+
MAX_LENGTH = int(os.environ.get('ZXCVBN_MAX_LENGTH', DEFAULT_MAX_LENGTH))
8+
59
def zxcvbn(password, user_inputs=None):
10+
# Throw error if password exceeds max length
11+
if len(password) > MAX_LENGTH:
12+
raise ValueError(f"Password length exceeds {MAX_LENGTH} characters.")
13+
614
try:
715
# Python 2 string types
816
basestring = (str, unicode)

0 commit comments

Comments
 (0)