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脚本
Apr 05 Python
Python multiprocessing.Manager介绍和实例(进程间共享数据)
Nov 21 Python
举例详解Python中的split()函数的使用方法
Apr 07 Python
python删除指定类型(或非指定)的文件实例详解
Jul 06 Python
简单掌握Python中glob模块查找文件路径的用法
Jul 05 Python
浅析python继承与多重继承
Sep 13 Python
分析运行中的 Python 进程详细解析
Jun 22 Python
python 实现GUI(图形用户界面)编程详解
Jul 17 Python
Python with关键字,上下文管理器,@contextmanager文件操作示例
Oct 17 Python
基于python求两个列表的并集.交集.差集
Feb 10 Python
python可迭代对象去重实例
May 15 Python
python pyg2plot的原理知识点总结
Feb 28 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
解析CodeIgniter自定义配置文件
2013/06/18 PHP
js跨域问题之跨域iframe自适应大小实现代码
2010/07/17 Javascript
关于jQuery参考实例 1.0 jQuery的哲学
2013/04/07 Javascript
jquery 通过name快速取值示例
2014/01/24 Javascript
网站基于flash实现的Banner图切换效果代码
2014/10/14 Javascript
js实现每日自动换一张图片的方法
2015/05/04 Javascript
javascript常用功能汇总
2015/07/05 Javascript
简单的jQuery入门指引
2015/07/28 Javascript
Jquery和BigFileUpload实现大文件上传及进度条显示
2016/06/27 Javascript
jQuery制作圣诞主题页面 更像是爱情影集
2016/08/10 Javascript
jQuery插件HighCharts绘制2D带Label的折线图效果示例【附demo源码下载】
2017/03/08 Javascript
基于Bootstrap框架实现图片切换
2017/03/10 Javascript
整理关于Bootstrap导航的慕课笔记
2017/03/29 Javascript
微信小程序实现倒计时60s获取验证码
2020/04/17 Javascript
React数据传递之组件内部通信的方法
2017/12/31 Javascript
Node.js搭建小程序后台服务
2018/01/03 Javascript
vue的diff算法知识点总结
2018/03/29 Javascript
微信分享invalid signature签名错误踩过的坑
2020/04/11 Javascript
[00:23]DOTA2群星共贺开放测试 25日无码时代来袭
2013/09/23 DOTA
Python实现的下载8000首儿歌的代码分享
2014/11/21 Python
python抓取百度首页的方法
2015/05/19 Python
python+numpy实现的基本矩阵操作示例
2019/07/19 Python
Atom Python 配置Python3 解释器的方法
2019/08/28 Python
python连接mongodb集群方法详解
2020/02/13 Python
python利用datetime模块计算程序运行时间问题
2020/02/20 Python
Keras模型转成tensorflow的.pb操作
2020/07/06 Python
python Selenium 库的使用技巧
2020/10/16 Python
CSS3制作酷炫的条纹背景
2017/11/09 HTML / CSS
澳大利亚设计的优质鞋类和适合澳大利亚生活方式的服装:Rivers
2019/04/23 全球购物
BudgetAir印度:预订航班、酒店和汽车租赁
2019/07/07 全球购物
韩语专业本科生求职信
2013/10/01 职场文书
降消项目实施方案
2014/03/30 职场文书
环境监测与治理技术专业求职信
2014/07/06 职场文书
2015年招生工作总结
2015/05/04 职场文书
我的中国梦心得体会范文
2016/01/05 职场文书
python 遍历磁盘目录的三种方法
2021/04/02 Python