网络编程
位置:首页>> 网络编程>> Python编程>> Django密码存储策略分析

Django密码存储策略分析

作者:蓝绿色~菠菜  发布时间:2022-03-10 04:16:33 

标签:Django,密码存储

一、源码分析

Django 发布的 1.4 版本中包含了一些安全方面的重要提升。其中一个是使用 PBKDF2 密码加密算法代替了 SHA1 。另外一个特性是你可以添加自己的密码加密方法。

Django 会使用你提供的第一个密码加密方法(在你的 setting.py 文件里要至少有一个方法)


PASSWORD_HASHERS = [
 'django.contrib.auth.hashers.PBKDF2PasswordHasher',
 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
 'django.contrib.auth.hashers.Argon2PasswordHasher',
 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
 'django.contrib.auth.hashers.BCryptPasswordHasher',
]

我们先一睹自带的PBKDF2PasswordHasher加密方式。


class BasePasswordHasher(object):
 """
 Abstract base class for password hashers
 When creating your own hasher, you need to override algorithm,
 verify(), encode() and safe_summary().
 PasswordHasher objects are immutable.
 """
 algorithm = None
 library = None

def _load_library(self):
   if self.library is not None:
     if isinstance(self.library, (tuple, list)):
       name, mod_path = self.library
     else:
       name = mod_path = self.library
     try:
       module = importlib.import_module(mod_path)
     except ImportError:
       raise ValueError("Couldn't load %s password algorithm "
                "library" % name)
     return module
   raise ValueError("Hasher '%s' doesn't specify a library attribute" %
            self.__class__)

def salt(self):
   """
   Generates a cryptographically secure nonce salt in ascii
   """
   return get_random_string()

def verify(self, password, encoded):
   """
   Checks if the given password is correct
   """
   raise NotImplementedError()

def encode(self, password, salt):
   """
   Creates an encoded database value
   The result is normally formatted as "algorithm$salt$hash" and
   must be fewer than 128 characters.
   """
   raise NotImplementedError()

def safe_summary(self, encoded):
   """
   Returns a summary of safe values
   The result is a dictionary and will be used where the password field
   must be displayed to construct a safe representation of the password.
   """
   raise NotImplementedError()

class PBKDF2PasswordHasher(BasePasswordHasher):
 """
 Secure password hashing using the PBKDF2 algorithm (recommended)
 Configured to use PBKDF2 + HMAC + SHA256.
 The result is a 64 byte binary string. Iterations may be changed
 safely but you must rename the algorithm if you change SHA256.
 """
 algorithm = "pbkdf2_sha256"
 iterations = 36000
 digest = hashlib.sha256

def encode(self, password, salt, iterations=None):
   assert password is not None
   assert salt and '$' not in salt
   if not iterations:
     iterations = self.iterations
   hash = pbkdf2(password, salt, iterations, digest=self.digest)
   hash = base64.b64encode(hash).decode('ascii').strip()
   return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash)

def verify(self, password, encoded):
   algorithm, iterations, salt, hash = encoded.split('$', 3)
   assert algorithm == self.algorithm
   encoded_2 = self.encode(password, salt, int(iterations))
   return constant_time_compare(encoded, encoded_2)

def safe_summary(self, encoded):
   algorithm, iterations, salt, hash = encoded.split('$', 3)
   assert algorithm == self.algorithm
   return OrderedDict([
     (_('algorithm'), algorithm),
     (_('iterations'), iterations),
     (_('salt'), mask_hash(salt)),
     (_('hash'), mask_hash(hash)),
   ])

def must_update(self, encoded):
   algorithm, iterations, salt, hash = encoded.split('$', 3)
   return int(iterations) != self.iterations

def harden_runtime(self, password, encoded):
   algorithm, iterations, salt, hash = encoded.split('$', 3)
   extra_iterations = self.iterations - int(iterations)
   if extra_iterations > 0:
     self.encode(password, salt, extra_iterations)

正如你看到那样,你必须继承自BasePasswordHasher,并且重写 verify() , encode() 以及 safe_summary() 方法。

Django 是使用 PBKDF 2算法与36,000次的迭代使得它不那么容易被暴力破解法轻易攻破。密码用下面的格式储存:

algorithm$number of iterations$salt$password hash”

例:pbkdf2_sha256$36000$Lx7auRCc8FUI$eG9lX66cKFTos9sEcihhiSCjI6uqbr9ZrO+Iq3H9xDU=

二、自定义密码加密方法

1、在settings.py中加入自定义的加密算法:


PASSWORD_HASHERS = [
 'myproject.hashers.MyMD5PasswordHasher',
 'django.contrib.auth.hashers.PBKDF2PasswordHasher',
 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
 'django.contrib.auth.hashers.Argon2PasswordHasher',
 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
 'django.contrib.auth.hashers.BCryptPasswordHasher',
]

2、再来看MyMD5PasswordHasher,这个是我自定义的加密方式,就是基本的md5,而django的MD5PasswordHasher是加盐的:


from django.contrib.auth.hashers import BasePasswordHasher,MD5PasswordHasher
from django.contrib.auth.hashers import mask_hash
import hashlib

class MyMD5PasswordHasher(MD5PasswordHasher):
  algorithm = "mymd5"

def encode(self, password, salt):
    assert password is not None
    hash = hashlib.md5(password).hexdigest().upper()
    return hash

def verify(self, password, encoded):
    encoded_2 = self.encode(password, '')
    return encoded.upper() == encoded_2.upper()

def safe_summary(self, encoded):
    return OrderedDict([
        (_('algorithm'), algorithm),
        (_('salt'), ''),
        (_('hash'), mask_hash(hash)),
        ])

之后可以在数据库中看到,密码确实使用了自定义的加密方式。

3、修改认证方式


AUTHENTICATION_BACKENDS = (
 'framework.mybackend.MyBackend', #新加
 'django.contrib.auth.backends.ModelBackend',
 'guardian.backends.ObjectPermissionBackend',
)

4、再来看自定义的认证方式


framework.mybackend.py:

import hashlib
from pro import models
from django.contrib.auth.backends import ModelBackend

class MyBackend(ModelBackend):
  def authenticate(self, username=None, password=None):
    try:
      user = models.M_User.objects.get(username=username)
      print user
    except Exception:
      print 'no user'
      return None
    if hashlib.md5(password).hexdigest().upper() == user.password:
      return user
    return None

def get_user(self, user_id):
    try:
      return models.M_User.objects.get(id=user_id)
    except Exception:
      return None

当然经过这些修改后最终的安全性比起django自带的降低很多,但是需求就是这样的,必须满足。

来源:https://blog.csdn.net/bocai_xiaodaidai/article/details/103872716

0
投稿

猜你喜欢

手机版 网络编程 asp之家 www.aspxhome.com