Python 开发中,使用bcrypt 或 Passlib 对系统用户密码进行哈希和验证处理
在设计一个系统的时候,肯定都有会有用户身份认证的问题,一般对用户校验的时候,都是对用户存在数据库总的密码哈希值进行判断,从而避免密码泄露和反向解密,那么在Python 开发中,我们可以引入bcrypt 或 Passlib 对系统用户密码进行哈希和验证处理,以及介绍使用其他类库实现常规加解密处理操作。本篇随笔主要介绍bcrypt 和 Passlib 它们之间的差异,以及在实际使用中的一些代码供参考。
1、bcrypt
和 Passlib的介绍
bcrypt
和 Passlib
都是用于密码哈希和验证的 Python 库,但它们有一些显著的区别:
bcrypt:
bcrypt
是一个专门用于实现bcrypt
哈希算法的库。它相对简单,专注于单一功能,即对密码进行bcrypt
哈希处理和验证。- 适合只需要
bcrypt
哈希算法的场景。 - 提供的 API 简单直接,功能较少。
Passlib:
Passlib
是一个更高级的密码哈希库,它支持多种哈希算法(如bcrypt
、PBKDF2
、Argon2
等),并且提供了更丰富的功能。- 适合需要支持多种密码哈希算法和策略的场景。
- 提供的
CryptContext
类可以方便地管理和迁移多个哈希算法。还提供了密码哈希的自动升级机制,以及对旧算法的弃用处理。
当你确定只需要使用 bcrypt
算法,并且不需要额外的复杂功能时,bcrypt
是一个合适的选择。它适合简单的项目,或者在需要直接控制 salt
等参数的情况下使用。
Passlib 适合复杂的项目,尤其是需要支持多个哈希算法或需要迁移哈希算法的场景。适合需要长期维护的项目,因为它提供了更多的配置和安全功能。
bcrypt: 灵活性较低,因为它只支持 bcrypt
算法。没有多种哈希算法选择或密码策略管理功能。使用简单,代码更直观。如果你只需要 bcrypt
算法,bcrypt
库可能更容易上手。
Passlib:提供了很高的灵活性和扩展性。可以根据需要切换和配置不同的哈希算法,管理复杂的密码策略。通过 CryptContext
,可以轻松管理不同算法之间的过渡。功能强大但相对复杂,需要更深入的学习和理解。但它的高层 API 设计得很友好,一旦熟悉,可以简化很多常见任务。CryptContext
是其中一个用于管理多个哈希算法和密码哈希策略的类。
示例代码对比:
bcrypt 使用示例:
import bcrypt password = b"supersecretpassword"
hashed = bcrypt.hashpw(password, bcrypt.gensalt()) # 验证密码
if bcrypt.checkpw(password, hashed):
print("Password matches!")
else:
print("Password does not match.")
Passlib 使用示例:
from passlib.context import CryptContext # 创建一个 CryptContext 对象
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") # 哈希密码
password = "my_secret_password"
hashed_password = pwd_context.hash(password)
print("Hashed password:", hashed_password) # 验证密码
is_correct = pwd_context.verify(password, hashed_password)
if is_correct:
print("密码正确")
else:
print("密码错误")
定义了一个 CryptContext
对象,用于管理密码哈希算法。schemes=["bcrypt"]
表示你要使用 bcrypt
算法,而 deprecated="auto"
表示自动管理过时的哈希方案。
使用 pwd_context.hash()
方法对密码进行哈希处理。每次生成的哈希值都是唯一的,即使是相同的密码也会生成不同的哈希值。
使用 pwd_context.verify()
方法可以验证给定的密码与存储的哈希值是否匹配。
你还可以在创建 CryptContext
对象时传递更多参数来定制密码哈希行为,这种方法可以增强密码存储的安全性。例如:
pwd_context = CryptContext(
schemes=["bcrypt"],
bcrypt__rounds=12 # bcrypt 的哈希轮数,默认为 12
)
2、使用指定的salt进行加密
在 Passlib
中,bcrypt
算法默认会自动生成一个随机的 salt
,这也是 bcrypt
的一种安全特性。如果你想使用指定的 salt
进行加密,需要注意的是,Passlib
并不直接支持通过指定 salt
来进行哈希处理,因为这可能会降低安全性。
不过,如果你确实需要使用指定的 salt
进行哈希处理,你可以使用以下的方式:
手动拼接
salt
和密码:可以手动拼接salt
和密码,然后对结果进行哈希处理。但这种方法仅适用于了解风险并确保安全措施的场景。使用
bcrypt
库:直接使用bcrypt
库进行处理,它允许你传递一个指定的salt
。不过,注意这会有一定的安全风险。
1) 使用 bcrypt
库指定 salt
如果你确实需要指定 salt
,可以使用 bcrypt
库。
import bcrypt # 指定的 salt(必须为 16 字节,前缀为 b"$2b$")
salt = bcrypt.gensalt(rounds=12) # 或者使用自定义的 16 字节 salt
print(f"Generated salt: {salt}") # 要加密的密码
password = "my_secret_password" # 使用指定的 salt 进行加密
hashed_password = bcrypt.hashpw(password.encode('utf-8'), salt)
print(f"Hashed password: {hashed_password}")
2) 手动拼接 salt
和密码
如果你使用 Passlib
,并想使用指定的 salt
,可以手动拼接 salt
和密码,然后对这个组合结果进行哈希处理。这个方式一般不建议使用,因为它破坏了 bcrypt
的安全设计原则。
from passlib.context import CryptContext # 创建一个 CryptContext 对象
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") # 自定义 salt
custom_salt = "my_custom_salt" # 拼接 salt 和密码
password = "my_secret_password"
password_with_salt = custom_salt + password # 对拼接后的字符串进行哈希处理
hashed_password = pwd_context.hash(password_with_salt)
print("Hashed password with custom salt:", hashed_password)
注意事项
- 使用固定的
salt
会降低密码哈希的安全性,因为相同的salt
和相同的密码会生成相同的哈希值。 bcrypt
的设计初衷是让每次生成的salt
都不同,以此提高安全性。- 如果你需要在特定的场景下使用固定的
salt
,一定要确保你的系统有足够的其他安全措施。
同一密码,每次获得的hash值都会不同,那么有些人会问,如果通过pwd_context.hash获得的hash值,下一次能够对比正确吗?
回答是的,使用 pwd_context.hash()
生成的哈希值可以在后续对比中正确匹配,即使每次生成的哈希值看起来不同。Passlib
和 bcrypt
的设计确保了这一点。
自动生成的
salt
:每次你使用pwd_context.hash()
生成一个新的哈希值时,bcrypt
都会自动生成一个随机的salt
并将其嵌入到生成的哈希值中。因此,即使对同一个密码进行多次哈希,每次生成的哈希值也会不同。验证过程:在验证过程中,
pwd_context.verify()
会自动从存储的哈希值中提取salt
并重新计算哈希,然后将其与提供的哈希值进行比较。这意味着,即使哈希值不同,验证仍然能够成功匹配。
即使你每次运行 pwd_context.hash(password)
得到的哈希值不同(因为 salt
不同),pwd_context.verify(password, hashed_password)
仍然会返回 True
,表示密码验证成功。
3、加密和解密处理
Passlib
主要用于密码哈希处理,并不支持加密和解密操作。如果你需要对字符串进行加密和解密,或者使用非对称加密,你需要使用其他库,例如 cryptography
或 PyCryptodome
。
1)对称加密和解密
对于对称加密,你可以使用 cryptography
库中的 Fernet
,它是基于 AES 算法的加密方案。
安装 cryptography
库
pip install cryptography
对称加密和解密示例
from cryptography.fernet import Fernet # 生成密钥(注意:密钥需要安全存储)
key = Fernet.generate_key()
cipher = Fernet(key) # 加密
message = "This is a secret message"
encrypted_message = cipher.encrypt(message.encode())
print("Encrypted:", encrypted_message) # 解密
decrypted_message = cipher.decrypt(encrypted_message).decode()
print("Decrypted:", decrypted_message)
2) 非对称加密和解密
对于非对称加密,你可以使用 cryptography
库中的 RSA
算法。通常,非对称加密用于加密较短的信息或加密对称密钥。
非对称加密和解密示例
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes # 生成私钥和公钥
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
) public_key = private_key.public_key() # 加密
message = b"This is a secret message"
encrypted_message = public_key.encrypt(
message,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
print("Encrypted:", encrypted_message) # 解密
decrypted_message = private_key.decrypt(
encrypted_message,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
print("Decrypted:", decrypted_message.decode())
3)保存和加载密钥
保存私钥:
private_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption()
)
with open('private_key.pem', 'wb') as f:
f.write(private_pem)
加载私钥:
with open('private_key.pem', 'rb') as f:
private_key = serialization.load_pem_private_key(
f.read(),
password=None,
)
保存公钥:
public_pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
with open('public_key.pem', 'wb') as f:
f.write(public_pem)
加载公钥:
with open('public_key.pem', 'rb') as f:
public_key = serialization.load_pem_public_key(f.read())
我们在开发过程总,可以根据需求选择合适的加密方式和库,并妥善管理密钥。
Python 开发中,使用bcrypt 或 Passlib 对系统用户密码进行哈希和验证处理的更多相关文章
- 系统开发中按下Enter键登录系统
转载来自:http://www.jb51.net/article/54308.htm 系统开发中按下Enter键登录系统,即就是监听键盘,当按下Enter键后调用登录按钮的click()事件. JS方 ...
- AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题
AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子. 一 AOP的基本概念 (1)Asp ...
- SharePoint 2010中重置windows 活动目录(AD)域用户密码的WebPart(免费下载)
由于SharePoint 2013推出不久,并非所有的企业都会升级到SharePoint 2013的,毕竟升级不是打打补丁这么简单,更多的企业还是使用Sharepoint 2010版本的,因此本人自行 ...
- SharePoint 2013中修改windows 活动目录(AD)域用户密码的WebPart(免费下载)
前段时间工作很忙,好久没更新博客了,趁国庆休假期间,整理了两个之前积累很实用的企业集成组件,并在真正的大型项目中经受住了考验:.Net版SAP RFC适配器组件和SharePoint 2013修改AD ...
- 重设windows10中的sub linux系统用户密码
原文:重设windows10中的sub linux系统用户密码 版权声明:本文为博主原创文章,转载请注明出处. https://blog.csdn.net/haiyoung/article/detai ...
- Android开发中使用Intent跳转到系统应用中的拨号界面、联系人界面、短信界面
现在开发中的功能需要直接跳转到拨号.联系人.短信界面等等,查找了很多资料,自己整理了一下. 首先,我们先看拨号界面,代码如下: Intent intent =new Intent(); intent. ...
- python开发基础作业01:模拟登陆系统
随老男孩学习python mark 作业要求及提示:编写登录接口 ''' 练习程序:编写登录接口 1. 输入用户名和密码 2. 认证成功后显示欢迎信息 3. 输错三次后锁定 输入三次后退出,下次同样用 ...
- PHP实现微信开发中提现功能(企业付款到用户零钱)
一.实现该功能目的 这几天在小程序里要实现用户从系统中提现到零钱的功能,查了一下文档可以使用 企业付款到用户零钱 来实现: 官方文档:https://pay.weixin.qq.com/wiki/do ...
- laravel5的Bcrypt加密方式对系统保存密码的小结
laravel5文档介绍 //对 A 密码使用Bcrypt 加密 $password = Hash::make('secret'); //你也可直接使用 bcrypt 的 function $pass ...
- laravel5的Bcrypt加密方式对系统保存密码
laravel5文档介绍 //对 A 密码使用Bcrypt 加密 $password = Hash::make('mima'); //你也可直接使用 bcrypt 的 function $passwo ...
随机推荐
- Controller-runtime模块
Controller-runtime框架 Controller-runtime是社区提供的用于开发Controller的框架,包含了各种已封装的代码库.Kubebuilder与Operator SDK ...
- Kubernetes(五) Pod控制器详解
Pod控制器详解 本章主要介绍Pod控制器的详细使用 1. Pod控制器介绍 在kubernetes中,按照pod的创建方式可以将其分为2类: 自主式pod:kubernetes直接创建出来的pod, ...
- 基于SQLite3的C学习总结
背景 针对 SQLite3 的学习总结 arm linux 移植 SQLite 3 如何在 Linux 上移植使用 SQLite3,标题虽然是在讲 arm linux,但实际上是跨平台的. 基于 SQ ...
- arm-gcc 命名规则
背景 在 Ubuntu中交叉编译裸机程序的时候,注意到了这个问题:因此对此作为一个总结. 命名规则 交叉编译工具链的命名规则为:arch [-vendor] [-os] [-(gnu)eabi] ar ...
- 使用Nginx在80端口上代理多个.NET CORE网站
有两个.NET CORE3.1网站部署在CentOS7上(内网IP是192.168.2.32),现在想实现访问http://192.168.2.32时访问A网站,访问http://192.168.2. ...
- position的值, relative和absolute分别是相对于谁进行定位的?
relative: 相对定位,相对于自己本身在正常文档流中的位置进行定位 相对它原来的位置,在走100px.原来在标准流中的位置继续占有. absolute: 生成绝对定位,相对于最近一级定位不为s ...
- 建立Model
直接使用Sequelize虽然可以,但是存在一些问题. 团队开发时,有人喜欢自己加timestamp: var Pet = sequelize.define('pet', { id: { type: ...
- [oeasy]python0015_键盘改造_将esc和capslock对调_hjkl_移动_双手正位
键盘改造 回忆上次内容 上次练习了复制粘贴 按键 作用 <kbd>y</kbd><kbd>y</kbd> 复制光标行代码 到剪贴板 <kbd> ...
- 学习 React 需要具备的 JavaScript 知识
学习 React 需要具备的 JavaScript 知识 为什么要学习 React? React 可以与任何其他库或框架无缝集成,因为 React 是一个仅视图库(它是 Model View C on ...
- Known框架实战演练——进销存业务单据
本文介绍如何实现进销存管理系统的业务单据模块,业务单据模块包括采购进货单.采购退货单.销售出货单.销售退货单4个菜单页面.由于进销单据字段大同小异,因此设计共用一个页面组件类. 项目代码:JxcLit ...