Django密码存储策略分析


Posted in Python onJanuary 09, 2020

一、源码分析

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自带的降低很多,但是需求就是这样的,必须满足。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python网络编程学习笔记(六):Web客户端访问
Jun 09 Python
详解Python验证码识别
Jan 25 Python
分享Python开发中要注意的十个小贴士
Aug 30 Python
Python 使用SMTP发送邮件的代码小结
Sep 21 Python
详解Python中最难理解的点-装饰器
Apr 03 Python
Python基础知识_浅谈用户交互
May 31 Python
解决Pycharm出现的部分快捷键无效问题
Oct 22 Python
python飞机大战pygame游戏背景设计详解
Dec 17 Python
如何通过python实现人脸识别验证
Jan 17 Python
使用pyplot.matshow()函数添加绘图标题
Jun 16 Python
python爬虫爬取图片的简单代码
Jan 18 Python
Python tkinter之ComboBox(下拉框)的使用简介
Feb 05 Python
python 实现Flask中返回图片流给前端展示
Jan 09 #Python
Python注释、分支结构、循环结构、伪“选择结构”用法实例分析
Jan 09 #Python
python将图片转base64,实现前端显示
Jan 09 #Python
Python 解码Base64 得到码流格式文本实例
Jan 09 #Python
Python变量、数据类型、数据类型转换相关函数用法实例详解
Jan 09 #Python
Python+OpenCV实现旋转文本校正方式
Jan 09 #Python
Python 实现OpenCV格式和PIL.Image格式互转
Jan 09 #Python
You might like
咖啡与牛奶
2021/03/03 冲泡冲煮
php中OR与|| AND与&&的区别总结
2013/10/26 PHP
php设计模式之单例模式使用示例
2014/01/20 PHP
linux平台编译安装PHP7并安装Redis扩展与Swoole扩展实例教程
2016/09/30 PHP
php发送http请求的常用方法分析
2016/11/08 PHP
Javascript 二维数组
2009/11/26 Javascript
jQuery操作CheckBox的方法介绍(选中,取消,取值)
2014/02/04 Javascript
node.js中的console.trace方法使用说明
2014/12/09 Javascript
js精美的幻灯片画集特效代码分享
2015/08/29 Javascript
jquery实现简单合拢与展开网页面板的方法
2015/09/01 Javascript
javascript操作cookie
2017/01/17 Javascript
使用bootstrap插件实现模态框效果
2017/05/10 Javascript
JavaScript变量作用域_动力节点Java学院整理
2017/06/27 Javascript
Node.js 使用递归实现遍历文件夹中所有文件
2017/09/18 Javascript
JavaScript内存泄漏的处理方式
2017/11/20 Javascript
Ionic学习日记实现验证码倒计时
2018/02/08 Javascript
vue在手机中通过本机IP地址访问webApp的方法
2018/08/15 Javascript
Layui弹出层 加载 做编辑页面的方法
2019/09/16 Javascript
React+Redux实现简单的待办事项列表ToDoList
2019/09/29 Javascript
[43:14]Liquid vs Optic 2018国际邀请赛淘汰赛BO3 第二场 8.21
2018/08/22 DOTA
剖析Python的Tornado框架中session支持的实现代码
2015/08/21 Python
python实现解数独程序代码
2017/04/12 Python
基于python中theano库的线性回归
2018/08/31 Python
10个Python小技巧你值得拥有
2018/09/29 Python
Python sys模块常用方法解析
2020/02/20 Python
python opencv 实现读取、显示、写入图像的方法
2020/06/08 Python
使用Nibabel库对nii格式图像的读写操作
2020/07/01 Python
定义css设备类型-Media Queries图表简介及使用方法
2013/01/21 HTML / CSS
html5 拖拽上传图片实例演示
2013/04/01 HTML / CSS
HTML5 Canvas实现平移/放缩/旋转deom示例(附截图)
2013/07/04 HTML / CSS
如何写好升职自荐信
2014/01/06 职场文书
材料加工工程求职信
2014/02/19 职场文书
Jupyter notebook 输出部分显示不全的解决方案
2021/04/24 Python
PyTorch dropout设置训练和测试模式的实现
2021/05/27 Python
安装配置mysql及Navicat prenium的详细流程
2021/06/10 MySQL
python如何为list实现find方法
2022/05/30 Python