04/27 Fixed some issues 2
This commit is contained in:
+13
-2
@@ -69,9 +69,17 @@ class User(UserMixin, db.Model):
|
||||
|
||||
@staticmethod
|
||||
def verify_set_password_token(token):
|
||||
"""Return the User whose token matches, or None if invalid/expired."""
|
||||
"""Return the User whose token matches, or None if invalid/expired.
|
||||
|
||||
The final token comparison uses hmac.compare_digest so that the
|
||||
comparison runs in constant time regardless of how many characters
|
||||
match, preventing timing-based token enumeration attacks.
|
||||
"""
|
||||
import hmac
|
||||
if not token:
|
||||
return None
|
||||
# Primary lookup is via DB index — compare_digest is a defense-in-depth
|
||||
# guard applied after the row is retrieved to harden the string comparison.
|
||||
user = User.query.filter_by(set_password_token=token).first()
|
||||
if user is None:
|
||||
return None
|
||||
@@ -79,7 +87,10 @@ class User(UserMixin, db.Model):
|
||||
return None
|
||||
if now_eastern() > user.set_password_token_expires:
|
||||
return None
|
||||
# Constant-time comparison — prevents timing oracle on the stored token
|
||||
if not hmac.compare_digest(user.set_password_token, token):
|
||||
return None
|
||||
return user
|
||||
|
||||
def __repr__(self):
|
||||
return f'<User {self.username}>'
|
||||
return f'<User {self.username}>'
|
||||
Reference in New Issue
Block a user