首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

Python 开发中,使用bcrypt 或 Passlib 对系统用户密码进行哈希和验证处理

编程知识
2024年08月21日 10:26

在设计一个系统的时候,肯定都有会有用户身份认证的问题,一般对用户校验的时候,都是对用户存在数据库总的密码哈希值进行判断,从而避免密码泄露和反向解密,那么在Python 开发中,我们可以引入bcrypt 或 Passlib 对系统用户密码进行哈希和验证处理,以及介绍使用其他类库实现常规加解密处理操作。本篇随笔主要介绍bcrypt 和 Passlib 它们之间的差异,以及在实际使用中的一些代码供参考。

1、bcryptPasslib的介绍

bcryptPasslib 都是用于密码哈希和验证的 Python 库,但它们有一些显著的区别:

  • bcrypt:

    • bcrypt 是一个专门用于实现 bcrypt 哈希算法的库。它相对简单,专注于单一功能,即对密码进行 bcrypt 哈希处理和验证。
    • 适合只需要 bcrypt 哈希算法的场景。
    • 提供的 API 简单直接,功能较少。
  • Passlib:

    • Passlib 是一个更高级的密码哈希库,它支持多种哈希算法(如 bcryptPBKDF2Argon2 等),并且提供了更丰富的功能。
    • 适合需要支持多种密码哈希算法和策略的场景。
    • 提供的 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 进行哈希处理,你可以使用以下的方式:

  1. 手动拼接 salt 和密码:可以手动拼接 salt 和密码,然后对结果进行哈希处理。但这种方法仅适用于了解风险并确保安全措施的场景。

  2. 使用 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() 生成的哈希值可以在后续对比中正确匹配,即使每次生成的哈希值看起来不同。Passlibbcrypt 的设计确保了这一点。

  • 自动生成的 salt:每次你使用 pwd_context.hash() 生成一个新的哈希值时,bcrypt 都会自动生成一个随机的 salt 并将其嵌入到生成的哈希值中。因此,即使对同一个密码进行多次哈希,每次生成的哈希值也会不同。

  • 验证过程:在验证过程中,pwd_context.verify() 会自动从存储的哈希值中提取 salt 并重新计算哈希,然后将其与提供的哈希值进行比较。这意味着,即使哈希值不同,验证仍然能够成功匹配。

即使你每次运行 pwd_context.hash(password) 得到的哈希值不同(因为 salt 不同),pwd_context.verify(password, hashed_password) 仍然会返回 True,表示密码验证成功。

 

3、加密和解密处理

Passlib 主要用于密码哈希处理,并不支持加密和解密操作。如果你需要对字符串进行加密和解密,或者使用非对称加密,你需要使用其他库,例如 cryptographyPyCryptodome

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())

 

我们在开发过程总,可以根据需求选择合适的加密方式和库,并妥善管理密钥。

 

From:https://www.cnblogs.com/wuhuacong/p/18371249
本文地址: http://shuzixingkong.net/article/1294
0评论
提交 加载更多评论
其他文章 零基础学习人工智能—Python—Pytorch学习(六)
前言 本文主要讲神经网络的上半部分。 另外,我发现我前面文章写的有歧义的地方还是挺多,虽然,已经改了一部分,但,可能还有没发现的,大家看的时候尽量多理解着看吧。 本着目的是学会使用神经网络的开发,至于数学的部分,就能过就过吧。 神经网络 先学个例子 先结合以前的知识理解一个例子,理解了这个例子,后面
零基础学习人工智能—Python—Pytorch学习(六) 零基础学习人工智能—Python—Pytorch学习(六) 零基础学习人工智能—Python—Pytorch学习(六)
SearXNG与LLM强强联合:打造用户隐私保护的智能搜索解答流程,隐私无忧,搜索无忧
SearXNG与LLM强强联合:打造用户隐私保护的智能搜索解答流程,隐私无忧,搜索无忧 SearXNG 是一个免费的互联网元搜索引擎,整合了各种搜索服务的结果。用户不会被跟踪,也不会被分析。 github地址:https://github.com/searxng/searxng 项目地址:https
SearXNG与LLM强强联合:打造用户隐私保护的智能搜索解答流程,隐私无忧,搜索无忧 SearXNG与LLM强强联合:打造用户隐私保护的智能搜索解答流程,隐私无忧,搜索无忧 SearXNG与LLM强强联合:打造用户隐私保护的智能搜索解答流程,隐私无忧,搜索无忧
智能客服的演变:从传统到向量数据库的新时代
向量数据库的崛起,为传统数据库提供了强有力的补充。它的优势在于处理复杂数据和高维数据时的高效性,尤其是在大规模数据分析、实时检索和智能推荐等领域表现突出。传统数据库在结构化数据和事务管理方面表现优异,但在非结构化数据处理、语义搜索和机器学习任务中的局限性逐渐显现。向量数据库的出现,不仅推动了数据存储
智能客服的演变:从传统到向量数据库的新时代 智能客服的演变:从传统到向量数据库的新时代 智能客服的演变:从传统到向量数据库的新时代
sign_and_send_pubkey: signing failed: agent refused operation
报错描述 ssh连接远程主机时,出现 sign_and_send_pubkey: signing failed: agent refused operation 错误,并且还是需要输入密码 实验环境 Master [root@kvm-master ~]# ssh-copy-id -i .ssh/id
sign_and_send_pubkey: signing failed: agent refused operation sign_and_send_pubkey: signing failed: agent refused operation sign_and_send_pubkey: signing failed: agent refused operation
【2】Kaggle 医学影像数据读取
赛题名称:RSNA 2024 Lumbar Spine Degenerative Classification 中文:腰椎退行性病变分类 kaggle官网赛题链接:https://www.kaggle.com/competitions/rsna-2024-lumbar-spine-degenerat
【2】Kaggle 医学影像数据读取 【2】Kaggle 医学影像数据读取 【2】Kaggle 医学影像数据读取
GC终结标记 SuspendEE 是怎么回事
一:背景 1. 讲故事 写这篇是起源于训练营里有位朋友提到了一个问题,在 !t -special 输出中有一个 SuspendEE 字样,这个字样在 coreclr 中怎么弄的?输出如下: 0:000> !t -special ThreadCount: 3 UnstartedThread: 0
GC终结标记 SuspendEE 是怎么回事 GC终结标记 SuspendEE 是怎么回事
Electron初体验
为什么使用electron 最近准备开发一个国产麒麟系统上的桌面程序,主要完成Linux命令的可视化,而electron作为目前最活跃的跨平台桌面应用程序的框架之一,它可以使用网页技术(如 HTML、CSS 和 JavaScript)来创建桌面应用程序,同时利用 Node.js 的强大能力来访问操作
Electron初体验 Electron初体验
源码解析之为何要用ConcurrentHashMap
为什么要用ConcurrentHashMap? ConcurrentHashMap是JUC包下的一个线程安全的HashMap类,我们都知道多线程的场景下要用ConcurrentHashMap来代替HashMap使用,有没有想过为什么不能用HashMap,为什么能用ConcurrentHashMap呢
源码解析之为何要用ConcurrentHashMap 源码解析之为何要用ConcurrentHashMap 源码解析之为何要用ConcurrentHashMap