mirror of
https://github.com/wassname/flask-security.git
synced 2026-06-27 16:10:11 +08:00
Add more secure password storage via salt value and hmac
This commit is contained in:
+1
-1
@@ -114,7 +114,7 @@ using SQLAlchemy.::
|
||||
class User(db.Model, UserMixin):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
email = db.Column(db.String(255), unique=True)
|
||||
password = db.Column(db.String(120))
|
||||
password = db.Column(db.String(255))
|
||||
remember_token = db.Column(db.String(255))
|
||||
active = db.Column(db.Boolean())
|
||||
authentication_token = db.Column(db.String(255))
|
||||
|
||||
+2
-2
@@ -136,7 +136,7 @@ def create_sqlalchemy_app(auth_config=None, register_blueprint=True):
|
||||
class User(db.Model, UserMixin):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
email = db.Column(db.String(255), unique=True)
|
||||
password = db.Column(db.String(120))
|
||||
password = db.Column(db.String(255))
|
||||
remember_token = db.Column(db.String(255))
|
||||
last_login_at = db.Column(db.DateTime())
|
||||
current_login_at = db.Column(db.DateTime())
|
||||
@@ -180,7 +180,7 @@ def create_mongoengine_app(auth_config=None):
|
||||
|
||||
class User(db.Document, UserMixin):
|
||||
email = db.StringField(unique=True, max_length=255)
|
||||
password = db.StringField(required=True, max_length=120)
|
||||
password = db.StringField(required=True, max_length=255)
|
||||
remember_token = db.StringField(max_length=255)
|
||||
last_login_at = db.DateTimeField()
|
||||
current_login_at = db.DateTimeField()
|
||||
|
||||
@@ -21,7 +21,7 @@ from werkzeug.local import LocalProxy
|
||||
|
||||
from . import views, exceptions
|
||||
from .confirmable import requires_confirmation
|
||||
from .utils import config_value as cv, get_config
|
||||
from .utils import config_value as cv, get_config, verify_password
|
||||
|
||||
# Convenient references
|
||||
_security = LocalProxy(lambda: current_app.extensions['security'])
|
||||
@@ -32,6 +32,8 @@ _default_config = {
|
||||
'URL_PREFIX': None,
|
||||
'FLASH_MESSAGES': True,
|
||||
'PASSWORD_HASH': 'plaintext',
|
||||
'PASSWORD_SALT': None,
|
||||
'PASSWORD_HMAC': False,
|
||||
'AUTH_URL': '/auth',
|
||||
'LOGOUT_URL': '/logout',
|
||||
'REGISTER_URL': '/register',
|
||||
@@ -308,7 +310,9 @@ class AuthenticationProvider(object):
|
||||
raise exceptions.BadCredentialsError('Account requires confirmation')
|
||||
|
||||
# compare passwords
|
||||
if _security.pwd_context.verify(password, user.password):
|
||||
if verify_password(password, user.password,
|
||||
salt=_security.password_salt,
|
||||
use_hmac=_security.password_hmac):
|
||||
return user
|
||||
|
||||
# bad match
|
||||
|
||||
@@ -113,7 +113,10 @@ class UserDatastore(object):
|
||||
pw = kwargs['password']
|
||||
|
||||
if not pwd_context.identify(pw):
|
||||
kwargs['password'] = pwd_context.encrypt(pw)
|
||||
pwd_hash = utils.encrypt_password(pw,
|
||||
salt=_security.password_salt,
|
||||
use_hmac=_security.password_hmac)
|
||||
kwargs['password'] = pwd_hash
|
||||
|
||||
kwargs['remember_token'] = utils.get_remember_token(kwargs['email'],
|
||||
kwargs['password'])
|
||||
|
||||
@@ -77,7 +77,9 @@ def _check_http_auth():
|
||||
except UserNotFoundError:
|
||||
return False
|
||||
|
||||
rv = _security.pwd_context.verify(auth.password, user.password)
|
||||
rv = utils.verify_password(auth.password, user.password,
|
||||
salt=_security.password_salt,
|
||||
use_hmac=_security.password_hmac)
|
||||
|
||||
if rv:
|
||||
identity_changed.send(current_app._get_current_object(),
|
||||
|
||||
@@ -16,7 +16,7 @@ from werkzeug.local import LocalProxy
|
||||
from .exceptions import ResetPasswordError, UserNotFoundError
|
||||
from .signals import password_reset, password_reset_requested, \
|
||||
reset_instructions_sent
|
||||
from .utils import send_mail, get_max_age, md5, get_message
|
||||
from .utils import send_mail, get_max_age, md5, get_message, encrypt_password
|
||||
|
||||
|
||||
# Convenient references
|
||||
@@ -85,7 +85,10 @@ def reset_by_token(token, password):
|
||||
if md5(user.password) != data[1]:
|
||||
raise UserNotFoundError()
|
||||
|
||||
user.password = _security.pwd_context.encrypt(password)
|
||||
user.password = encrypt_password(password,
|
||||
salt=_security.password_salt,
|
||||
use_hmac=_security.password_hmac)
|
||||
print user.password
|
||||
_datastore._save_model(user)
|
||||
|
||||
send_password_reset_notice(user)
|
||||
|
||||
+19
-1
@@ -11,6 +11,7 @@
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import hmac
|
||||
import os
|
||||
from contextlib import contextmanager
|
||||
from datetime import datetime, timedelta
|
||||
@@ -25,6 +26,23 @@ from .signals import user_registered, password_reset_requested
|
||||
# Convenient references
|
||||
_security = LocalProxy(lambda: current_app.extensions['security'])
|
||||
|
||||
_pwd_context = LocalProxy(lambda: _security.pwd_context)
|
||||
|
||||
|
||||
def get_hmac(msg, salt=None, digestmod=None):
|
||||
digestmod = digestmod or hashlib.sha512
|
||||
return base64.b64encode(hmac.new(salt, msg, digestmod).digest())
|
||||
|
||||
|
||||
def verify_password(password, password_hash, salt=None, use_hmac=False):
|
||||
hmac_value = get_hmac(password, salt) if use_hmac else password
|
||||
return _pwd_context.verify(hmac_value, password_hash)
|
||||
|
||||
|
||||
def encrypt_password(password, salt=None, use_hmac=False):
|
||||
hmac_value = get_hmac(password, salt) if use_hmac else password
|
||||
return _pwd_context.encrypt(hmac_value)
|
||||
|
||||
|
||||
def md5(data):
|
||||
return hashlib.md5(data).hexdigest()
|
||||
@@ -202,4 +220,4 @@ def capture_reset_password_requests(reset_password_sent_at=None):
|
||||
try:
|
||||
yield reset_requests
|
||||
finally:
|
||||
password_reset_requested.disconnect(_on)
|
||||
password_reset_requested.disconnect(_on)
|
||||
@@ -157,6 +157,9 @@ class DefaultSecurityTests(SecurityTest):
|
||||
class ConfiguredSecurityTests(SecurityTest):
|
||||
|
||||
AUTH_CONFIG = {
|
||||
'SECURITY_PASSWORD_HASH': 'bcrypt',
|
||||
'SECURITY_PASSWORD_SALT': 'so-salty',
|
||||
'SECURITY_PASSWORD_HMAC': True,
|
||||
'SECURITY_REGISTERABLE': True,
|
||||
'SECURITY_AUTH_URL': '/custom_auth',
|
||||
'SECURITY_LOGOUT_URL': '/custom_logout',
|
||||
|
||||
Reference in New Issue
Block a user