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

打印 上一主题 下一主题

主题 902|帖子 902|积分 2706

在计划一个系统的时间,肯定都有会有用户身份认证的问题,一样平常对用户校验的时间,都是对用户存在数据库总的密码哈希值进行判断,从而避免密码泄漏和反向解密,那么在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 使用示例:
  1. import bcrypt
  2. password = b"supersecretpassword"
  3. hashed = bcrypt.hashpw(password, bcrypt.gensalt())
  4. # 验证密码
  5. if bcrypt.checkpw(password, hashed):
  6.     print("Password matches!")
  7. else:
  8.     print("Password does not match.")
复制代码
Passlib 使用示例:
  1. from passlib.context import CryptContext
  2. # 创建一个 CryptContext 对象
  3. pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
  4. # 哈希密码
  5. password = "my_secret_password"
  6. hashed_password = pwd_context.hash(password)
  7. print("Hashed password:", hashed_password)
  8. # 验证密码
  9. is_correct = pwd_context.verify(password, hashed_password)
  10. if is_correct:
  11.     print("密码正确")
  12. else:
  13.     print("密码错误")
复制代码
界说了一个 CryptContext 对象,用于管理密码哈希算法。schemes=["bcrypt"] 表示你要使用 bcrypt 算法,而 deprecated="auto" 表示主动管理过时的哈希方案。
使用 pwd_context.hash() 方法对密码进行哈希处理。每次天生的哈希值都是唯一的,纵然是相同的密码也会天生不同的哈希值。
使用 pwd_context.verify() 方法可以验证给定的密码与存储的哈希值是否匹配。
你还可以在创建 CryptContext 对象时通报更多参数来定制密码哈希行为,这种方法可以加强密码存储的安全性。例如:
  1. pwd_context = CryptContext(
  2.     schemes=["bcrypt"],
  3.     bcrypt__rounds=12  # bcrypt 的哈希轮数,默认为 12
  4. )
复制代码
2、使用指定的salt进行加密

在 Passlib 中,bcrypt 算法默认会主动天生一个随机的 salt,这也是 bcrypt 的一种安全特性。如果你想使用指定的 salt 进行加密,需要注意的是,Passlib 并不直接支持通过指定 salt 来进行哈希处理,因为这可能会低落安全性。
不外,如果你确实需要使用指定的 salt 进行哈希处理,你可以使用以下的方式:

  • 手动拼接 salt 和密码:可以手动拼接 salt 和密码,然后对结果进行哈希处理。但这种方法仅适用于了解风险并确保安全措施的场景。
  • 使用 bcrypt 库:直接使用 bcrypt 库进行处理,它允许你通报一个指定的 salt。不外,注意这会有肯定的安全风险。
1) 使用 bcrypt 库指定 salt

如果你确实需要指定 salt,可以使用 bcrypt 库。
  1. import bcrypt
  2. # 指定的 salt(必须为 16 字节,前缀为 b"$2b$")
  3. salt = bcrypt.gensalt(rounds=12)  # 或者使用自定义的 16 字节 salt
  4. print(f"Generated salt: {salt}")
  5. # 要加密的密码
  6. password = "my_secret_password"
  7. # 使用指定的 salt 进行加密
  8. hashed_password = bcrypt.hashpw(password.encode('utf-8'), salt)
  9. print(f"Hashed password: {hashed_password}")
复制代码
2) 手动拼接 salt 和密码

如果你使用 Passlib,并想使用指定的 salt,可以手动拼接 salt 和密码,然后对这个组合结果进行哈希处理。这个方式一样平常不建议使用,因为它粉碎了 bcrypt 的安全计划原则。
  1. from passlib.context import CryptContext
  2. # 创建一个 CryptContext 对象
  3. pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
  4. # 自定义 salt
  5. custom_salt = "my_custom_salt"
  6. # 拼接 salt 和密码
  7. password = "my_secret_password"
  8. password_with_salt = custom_salt + password
  9. # 对拼接后的字符串进行哈希处理
  10. hashed_password = pwd_context.hash(password_with_salt)
  11. 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 库
  1. pip install cryptography
复制代码
对称加密和解密示例
  1. from cryptography.fernet import Fernet
  2. # 生成密钥(注意:密钥需要安全存储)
  3. key = Fernet.generate_key()
  4. cipher = Fernet(key)
  5. # 加密
  6. message = "This is a secret message"
  7. encrypted_message = cipher.encrypt(message.encode())
  8. print("Encrypted:", encrypted_message)
  9. # 解密
  10. decrypted_message = cipher.decrypt(encrypted_message).decode()
  11. print("Decrypted:", decrypted_message)
复制代码
2) 非对称加密和解密

对于非对称加密,你可以使用 cryptography 库中的 RSA 算法。通常,非对称加密用于加密较短的信息或加密对称密钥。
非对称加密和解密示例
  1. from cryptography.hazmat.primitives.asymmetric import rsa, padding
  2. from cryptography.hazmat.primitives import serialization, hashes
  3. # 生成私钥和公钥
  4. private_key = rsa.generate_private_key(
  5.     public_exponent=65537,
  6.     key_size=2048,
  7. )
  8. public_key = private_key.public_key()
  9. # 加密
  10. message = b"This is a secret message"
  11. encrypted_message = public_key.encrypt(
  12.     message,
  13.     padding.OAEP(
  14.         mgf=padding.MGF1(algorithm=hashes.SHA256()),
  15.         algorithm=hashes.SHA256(),
  16.         label=None
  17.     )
  18. )
  19. print("Encrypted:", encrypted_message)
  20. # 解密
  21. decrypted_message = private_key.decrypt(
  22.     encrypted_message,
  23.     padding.OAEP(
  24.         mgf=padding.MGF1(algorithm=hashes.SHA256()),
  25.         algorithm=hashes.SHA256(),
  26.         label=None
  27.     )
  28. )
  29. print("Decrypted:", decrypted_message.decode())
复制代码
3)保存和加载密钥

保存私钥:
  1. private_pem = private_key.private_bytes(
  2.     encoding=serialization.Encoding.PEM,
  3.     format=serialization.PrivateFormat.TraditionalOpenSSL,
  4.     encryption_algorithm=serialization.NoEncryption()
  5. )
  6. with open('private_key.pem', 'wb') as f:
  7.     f.write(private_pem)
复制代码
加载私钥:
  1. with open('private_key.pem', 'rb') as f:
  2.     private_key = serialization.load_pem_private_key(
  3.         f.read(),
  4.         password=None,
  5.     )
复制代码
保存公钥:
  1. public_pem = public_key.public_bytes(
  2.     encoding=serialization.Encoding.PEM,
  3.     format=serialization.PublicFormat.SubjectPublicKeyInfo
  4. )
  5. with open('public_key.pem', 'wb') as f:
  6.     f.write(public_pem)
复制代码
加载公钥:
  1. with open('public_key.pem', 'rb') as f:
  2.     public_key = serialization.load_pem_public_key(f.read())
复制代码
 
我们在开发过程总,可以根据需求选择符合的加密方式和库,并妥善管理密钥。
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

美食家大橙子

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表