您的位置:首页 > 编程语言 > Python开发

Python Show-Me-the-Code 第 0021题 密码加密

2015-06-02 00:08 375 查看
第 0021 题: 通常,登陆某个网站或者 APP,需要使用用户名和密码。密码是如何加密后存储起来的呢?请使用 Python 对密码加密。

阅读资料 用户密码的存储与 Python 示例

阅读资料 Hashing Strings with Python

阅读资料 Python’s safest method to store and retrieve passwords from a database

思路:

加密技术是对信息进行编码和解码的技术,编码是把原来可读信息(又称明文)译成代码形式(又称密文),其逆过程就是解码(解密)。加密技术的要点是加密算法,加密算法可以分为对称加密、不对称加密和不可逆加密三类算法。

按照安全性由低到高,有这样几种选择:

密码明文直接存储在系统中

这种方法下密码的安全性比系统本身还低,管理员能查看所有用户的密码明文。除非是做恶意网站故意套取用户密码,否则不要用这种方式

密码明文经过转换后再存储

与直接存储明文的方式没有本质区别,任何知道或破解出转换方法的人都可以逆转换得到密码明文

密码经过对称加密后再存储

密码明文的安全性等同于加密密钥本身的安全性。对称加密的密钥可同时用于加密与解密。一般它会直接出现在加密代码中,破解的可能性相当大。而且系统管理员很可能知道密钥,进而算出密码原文

密码经过非对称加密后再存储

密码的安全性等同于私钥的安全性。密码明文经过公钥加密。要还原密码明文,必须要相应的私钥才行。因此只要保证私钥的安全,密码明文就安全。私钥可以由某个受信任的人或机构来掌管,身份验证只需要用公钥就可以了

实际上,这也是 HTTPS/SSL 的理论基础。这里的关键是 私钥的安全 ,如果私钥泄露,那密码明文就危险了。


以上 4 种方法的共同特点是可以从存储的密码形式还原到密码明文。

所以密码最好是以不可还原明文的方式来保存。通常利用哈希算法的单向性来保证明文以 不可还原的有损方式 进行存储。

这类方法的各个具体操作方式按安全性由低到高依次为:

使用自己独创的哈希算法对密码进行哈希,存储哈希过的值

哈希算法复杂,独创对理论要求很高。一般独创的哈希算法肯定没有公开经过时间检验的算法质量高,天才另算

使用 MD5 或 SHA-1 哈希算法

MD5 和 SHA-1 已破解。虽不能还原明文,但很容易找到能生成相同哈希值的替代明文。而且这两个算法速度较快,暴力破解相对省时,建议不要使用它们。

使用更安全的 SHA-256 等成熟算法

更加复杂的算法增加了暴力破解的难度。但如果遇到简单密码,用彩虹字典的暴力破解法,很快就能得到密码原文

加入随机 salt 的哈希算法

密码原文(或经过 hash 后的值)和随机生成的 salt 字符串混淆,然后再进行 hash,最后把 hash 值和 salt 值一起存储。验证密码的时候只要用 salt 再与密码原文做一次相同步骤的运算,比较结果与存储的 hash 值就可以了。这样一来哪怕是简单的密码,在进过 salt 混淆后产生的也是很不常见的字符串,根本不会出现在彩虹字典中。salt 越长暴力破解的难度越大


所以加入随机salt的哈希算法是一个安全性比较好的加密算法,能够废掉彩虹表,加入随机salt,每一个用户哪怕密码一样密文值也不一样。下面使用python实现这种加密方法。

代码:

0021.密码加密.py

#coding: utf-8
import random
from hashlib import sha256
from hmac import HMAC

def set_password(raw_password, salt=None):
    if salt is None:
        salt = sha256(str(random.random())).hexdigest()[-8:]
    if isinstance(raw_password, unicode):
        raw_password = raw_password.encode('utf-8')
    # password = sha256('%s%s' % (salt, raw_password)).hexdigest()
    password = HMAC(salt, raw_password, sha256).hexdigest()
    return ('%s$%s' % (salt, password))

def check_password(raw_password, enc_password):
    salt = enc_password.split('$')[0]
    return enc_password == set_password(raw_password, salt)

if __name__ == '__main__':
    raw_password1 = 'hehe'
    raw_password2 = 'test'
    salt = '12345678'
    enc_password1 = set_password(raw_password1)
    enc_password2 = set_password(raw_password2, salt)
    print 'enc_password1:%s' % enc_password1
    print 'enc_password2:%s' % enc_password2
    print check_password(raw_password1, enc_password1)
    print check_password(raw_password2, enc_password1)


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: