A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://github.com/django/django/commit/2135637fdd5ce994de110affef9e67dffdf77277 below:

[2.2.x] Fixed CVE-2021-45115 -- Prevented DoS vector in UserAttribute… · django/django@2135637 · GitHub

@@ -115,6 +115,36 @@ def get_help_text(self):

115 115

) % {'min_length': self.min_length}

116 116 117 117 118 +

def exceeds_maximum_length_ratio(password, max_similarity, value):

119 +

"""

120 +

Test that value is within a reasonable range of password.

121 + 122 +

The following ratio calculations are based on testing SequenceMatcher like

123 +

this:

124 + 125 +

for i in range(0,6):

126 +

print(10**i, SequenceMatcher(a='A', b='A'*(10**i)).quick_ratio())

127 + 128 +

which yields:

129 + 130 +

1 1.0

131 +

10 0.18181818181818182

132 +

100 0.019801980198019802

133 +

1000 0.001998001998001998

134 +

10000 0.00019998000199980003

135 +

100000 1.999980000199998e-05

136 + 137 +

This means a length_ratio of 10 should never yield a similarity higher than

138 +

0.2, for 100 this is down to 0.02 and for 1000 it is 0.002. This can be

139 +

calculated via 2 / length_ratio. As a result we avoid the potentially

140 +

expensive sequence matching.

141 +

"""

142 +

pwd_len = len(password)

143 +

length_bound_similarity = max_similarity / 2 * pwd_len

144 +

value_len = len(value)

145 +

return pwd_len >= 10 * value_len and value_len < length_bound_similarity

146 + 147 + 118 148

class UserAttributeSimilarityValidator:

119 149

"""

120 150

Validate whether the password is sufficiently different from the user's

@@ -130,19 +160,25 @@ class UserAttributeSimilarityValidator:

130 160 131 161

def __init__(self, user_attributes=DEFAULT_USER_ATTRIBUTES, max_similarity=0.7):

132 162

self.user_attributes = user_attributes

163 +

if max_similarity < 0.1:

164 +

raise ValueError('max_similarity must be at least 0.1')

133 165

self.max_similarity = max_similarity

134 166 135 167

def validate(self, password, user=None):

136 168

if not user:

137 169

return

138 170 171 +

password = password.lower()

139 172

for attribute_name in self.user_attributes:

140 173

value = getattr(user, attribute_name, None)

141 174

if not value or not isinstance(value, str):

142 175

continue

143 -

value_parts = re.split(r'\W+', value) + [value]

176 +

value_lower = value.lower()

177 +

value_parts = re.split(r'\W+', value_lower) + [value_lower]

144 178

for value_part in value_parts:

145 -

if SequenceMatcher(a=password.lower(), b=value_part.lower()).quick_ratio() >= self.max_similarity:

179 +

if exceeds_maximum_length_ratio(password, self.max_similarity, value_part):

180 +

continue

181 +

if SequenceMatcher(a=password, b=value_part).quick_ratio() >= self.max_similarity:

146 182

try:

147 183

verbose_name = str(user._meta.get_field(attribute_name).verbose_name)

148 184

except FieldDoesNotExist:


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4