Python代码安全实战:使用cryptography库实现签名与加密

📅 2026/6/30 19:01:26
Python代码安全实战:使用cryptography库实现签名与加密
1. 项目概述为什么Python开发者必须关注代码签名与加密最近在几个项目交付和代码审计的沟通过程中我反复被问到同一个问题“我们这段核心业务逻辑就这么用Python脚本发给客户/部署到服务器真的安全吗” 这个问题背后是很多开发者尤其是Python开发者长久以来的一个隐忧。Python作为一种解释型语言其源代码.py文件是“裸露”在外的。无论是商业软件交付、API密钥分发还是核心算法保护直接传递.py文件都意味着你的核心逻辑、配置信息甚至敏感数据对接收者是完全透明的。这就像把一封没封口的信寄出去途中的任何人都能一览无余。这不仅仅是商业机密的问题。想象一下你的脚本里硬编码了数据库密码、第三方服务的API密钥或者用于签名的私钥种子。一旦脚本泄露攻击者可以轻易获取这些凭据进而引发数据泄露、服务滥用甚至更严重的安全事件。我见过太多因为一个配置文件中明文密码而导致整个数据库被拖库的案例。因此为Python代码和数据“穿上衣服”——即进行签名与加密——从一个“锦上添花”的功能变成了一个严肃的生产级安全需求。那么具体要解决什么问题呢我认为核心有三点完整性验证代码签名确保你分发的脚本或数据在传输和存储过程中没有被篡改。接收方能够验证“这确实是你发出来的原版中间没人动过手脚”。这对于自动化部署、插件系统或任何需要信任链的场景至关重要。机密性保护数据加密确保敏感信息如配置、密钥、用户数据即使被不该看到的人拿到他也无法解读其内容。只有拥有正确密钥的授权方才能解密使用。身份认证签名验签结合以上两点通过数字签名不仅能验证完整性还能确认信息的来源身份。“这条指令确实来自可信的服务器A而不是伪装者B”。市面上Python的加密库不少比如古老的pycrypto已停止维护、PyNaCl绑定Libsodium很好但API较底层。而我今天要深入探讨的是Python生态中目前被广泛认为是最佳实践选择的cryptography库。它由Python软件基金会成员维护底层基于稳健的C库如OpenSSL提供了既安全又友好的高级API和必要的底层原语。对于绝大多数应用场景cryptography应该是你的首选。本文适合所有需要提升Python应用安全性的开发者无论你是要保护一个即将交付给客户的算法脚本还是想安全地管理自己项目中的敏感配置都能在这里找到可直接落地的方案。我们将绕过枯燥的理论直接进入实战用cryptography解决真实问题。2. 核心密码学概念与cryptography库选型解析在动手写代码之前花几分钟理清几个核心概念非常有必要。密码学容易让人望而生畏但用于我们日常开发抓住几个关键点就够了。cryptography库的设计也很好地体现了这种分层思想。2.1 对称加密 vs. 非对称加密场景决定选择这是最根本的区分选错了类型整个方案可能就南辕北辙。对称加密比如AESAdvanced Encryption Standard它使用同一把密钥进行加密和解密。你可以把它想象成一个带密码的盒子锁和开锁用的是同一个密码。优点速度快效率高适合加密大量数据如整个文件、数据库字段、网络通信流。缺点密钥分发是难题。你怎么安全地把这把密钥交给对方如果通过网络明文发送密钥本身就可能被截获。典型应用加密存储在本地的配置文件、加密数据库中的用户隐私字段、HTTPS通信中加密实际传输数据的“会话密钥”。非对称加密比如RSARivest–Shamir–Adleman或ECCElliptic Curve Cryptography它使用一对密钥公钥Public Key和私钥Private Key。公钥可以公开给任何人私钥必须严格保密。用公钥加密的数据只能用对应的私钥解密用私钥签名的数据可以用对应的公钥验证签名。优点解决了密钥分发问题。你可以放心地把公钥放在任何地方比如项目仓库里因为用公钥加密的数据只有持有私钥的你才能解开。缺点速度慢比对称加密慢几个数量级不适合加密大量数据。典型应用数字签名用私钥签名公钥验签、安全密钥交换比如协商出一个对称加密的会话密钥、加密非常小的数据如一个对称加密的密钥本身。在实际项目中我们常常混合使用两者发挥各自长处。例如最常见的场景使用非对称加密RSA来安全地传递一个随机生成的、一次性的对称加密密钥比如AES密钥。后续大量的数据通信则使用这个对称密钥进行快速的AES加密。cryptography库对这两种模式都提供了完美支持。2.2 哈希、签名与验签完整性的守护神这组概念关乎“数据是否被篡改”。哈希Hash将一个任意长度的数据消息映射成一个固定长度的、看似随机的字符串哈希值。关键特性是“单向性”和“雪崩效应”。输入哪怕只改一个比特输出的哈希值也会天差地别。常见的算法有SHA-256、SHA-3等。它用于验证数据完整性但不能验证来源。cryptography在hashes模块中提供。签名Sign为了同时验证完整性和来源就需要签名。过程是先对原始数据计算哈希值然后用私钥对这个哈希值进行加密这个过程就叫签名。生成的结果就是数字签名。验签Verify接收方拿到原始数据和数字签名后做两件事1) 用同样的哈希算法计算数据的哈希值2) 用公钥去解密那个数字签名得到发送方计算的哈希值。对比两个哈希值如果一致则证明数据完整且确实来自持有对应私钥的发送方。2.3 为什么是cryptography深入对比与生态位Python的加密库生态经历过混乱。早年流行的pycrypto由于长期无人维护存在潜在的安全风险已不推荐使用。PyNaCl是一个优秀的库它提供了更现代的、基于Curve25519等椭圆曲线的密码学原语API相对安全但在通用性和与传统系统的兼容性上稍逊一筹。cryptography的优势在于权威与活跃由Python软件基金会成员维护更新频繁积极响应安全漏洞。分层APIFernet / 对称加密提供了极其易用的高级API如Fernet几行代码就能实现安全的对称加密非常适合新手和常见场景。Hazmat也提供了低级的“危险材料”hazmat层让专家可以接触到底层原语进行更灵活的定制。库名提醒你使用这一层需要你知道自己在做什么。坚实的后端默认基于OpenSSL这是业界最广泛测试和部署的密码学库之一保证了算法的正确性和性能。功能全面涵盖了从对称加密AES、非对称加密RSAECC、哈希SHA2 SHA3、签名到密钥派生PBKDF2等几乎所有常用功能。注意安装cryptography时它会尝试编译本地扩展。在Windows上如果遇到编译问题最简单的方法是使用预编译的wheel包。通常直接pip install cryptography即可如果失败可以尝试从 此处 下载对应版本的whl文件安装。对于绝大多数应用从cryptography的高级API开始是安全性和开发效率的最佳平衡点。3. 实战使用cryptography实现对称加密与解密让我们从最常见的需求开始加密一段敏感信息比如数据库连接字符串或API密钥然后安全地存储或传输。这里我们将使用cryptography库中最简单的对称加密方案——Fernet。Fernet并非一个算法而是一个基于AES-128-CBC和HMAC-SHA256的对称加密规范。它帮你处理了加密模式、填充、初始化向量IV和完整性验证HMAC等细节你只需要关心密钥和要加密的数据。3.1 生成与管理Fernet密钥密钥是安全的核心。Fernet密钥是一个32字节的URL安全的base64编码字符串。from cryptography.fernet import Fernet # 生成一个密钥 key Fernet.generate_key() print(f生成的密钥 (请妥善保存): {key.decode()}) # 输出类似: bV2hhdF9hX2JldXRpZnVsX2tleSEhCg - V2hhdF9hX2JldXRpZnVsX2tleSEhCg密钥管理是重中之重绝对不要将密钥硬编码在源代码中尤其是提交到版本控制系统如Git。推荐做法环境变量将密钥存储在部署环境的环境变量中。# 在部署服务器上设置 export MY_APP_SECRET_KEYV2hhdF9hX2JldXRpZnVsX2tleSEhCg# 在代码中读取 import os key os.environ.get(MY_APP_SECRET_KEY).encode()密钥管理服务在生产环境中使用如HashiCorp Vault、AWS Secrets Manager、Azure Key Vault等服务来动态获取密钥。配置文件如果必须使用文件确保该文件有严格的访问权限如600并且绝不提交到代码库。可以通过首次运行时生成并提示用户保存。3.2 加密与解密数据有了密钥加解密就非常简单了。from cryptography.fernet import Fernet import os # 假设我们从环境变量获取密钥 key os.environ.get(MY_APP_SECRET_KEY, Fernet.generate_key()).encode() # 注意如果环境变量不存在这里会生成新密钥。生产环境应确保环境变量已设置。 cipher_suite Fernet(key) # 要加密的敏感数据必须是bytes类型 sensitive_data bDatabasePasswordSuperSecret123;APIKeyabc123def456 # 如果你有字符串需要编码sensitive_data my string.encode(utf-8) # 加密 encrypted_data cipher_suite.encrypt(sensitive_data) print(f加密后的数据 (可安全存储): {encrypted_data.decode()}) # 假设我们将 encrypted_data 存储到了文件或数据库现在要解密 decrypted_data cipher_suite.decrypt(encrypted_data) print(f解密后的数据: {decrypted_data.decode()})Fernet自动做了什么加密时它生成一个随机的128位初始化向量IV。使用AES-128-CBC模式和PKCS7填充加密数据。使用HMAC-SHA256为“IV 密文”计算一个认证标签MAC。最终输出 IV 密文 MAC。这个组合体就是encrypted_data。解密时它先验证MAC确保数据完整且未被篡改然后再解密。如果MAC验证失败会抛出cryptography.fernet.InvalidToken异常。实操心得Fernet.encrypt()输入和输出都是bytes。如果你要加密文本记得先用.encode(utf-8)转换。同样解密后得到bytes需要用.decode(utf-8)转回字符串。这虽然基础但却是新手最容易忽略导致报错的地方。3.3 进阶使用AES-GCM模式进行带关联数据的认证加密Fernet很好但有时你需要更灵活的控制或者想使用更新的算法模式。AES-GCMGalois/Counter Mode是目前推荐的一种认证加密模式它同时提供机密性、完整性和认证性并且通常比CBCHMAC的模式更高效。cryptography的hazmat.primitives层提供了这个能力。from cryptography.hazmat.primitives.ciphers.aead import AESGCM import os # 生成一个随机密钥AES-256-GCM需要32字节密钥 key AESGCM.generate_key(bit_length256) # 也可以是128或192位 # 创建AESGCM实例 aesgcm AESGCM(key) # 要加密的数据 data bSensitive configuration data here # 生成一个随机Nonce一次性数字GCM通常要求12字节 nonce os.urandom(12) # 可选的关联数据Associated Data, AD用于认证但不加密。例如可以放数据头、协议版本号。 associated_data bprotocol_version_1.0 # 加密 encrypted_data aesgcm.encrypt(nonce, data, associated_data) # 输出包含密文 认证标签。nonce需要单独保存。 print(fNonce (需与密文一起存储): {nonce.hex()}) print(f加密后的数据: {encrypted_data.hex()}) # 解密 try: decrypted_data aesgcm.decrypt(nonce, encrypted_data, associated_data) print(f解密成功: {decrypted_data.decode()}) except Exception as e: print(f解密失败数据可能被篡改或密钥错误: {e})关键点解析Nonce必须唯一。对于同一个密钥绝对不要重复使用同一个Nonce否则会严重破坏安全性。通常每次加密都随机生成并随密文一起存储/传输。关联数据AD这是一个非常强大的特性。它允许你将一些明文上下文如数据包头部、消息类型绑定到加密过程中这些数据会被认证确保未被篡改但不会被加密。解密时必须提供相同的AD才能成功。这可以防止攻击者将一种类型的密文重放到另一种类型的上下文中。注意事项使用hazmat层意味着你需要自己管理更多细节如Nonce的生成和唯一性、密钥生命周期。除非你有明确理由如特定协议要求、性能调优否则对于大多数应用使用Fernet是更安全、更省心的选择。Fernet帮你妥善处理了这些底层细节。4. 实战使用非对称加密RSA进行数字签名与验签现在我们来解决身份认证和完整性验证的问题。假设你开发了一个自动更新系统服务器发布一个新版本的脚本客户端需要下载并执行。客户端如何确信这个脚本来自可信的服务器且没有被中间人篡改这就需要数字签名。我们将使用RSA算法一种非对称加密算法来实现。在实际应用中椭圆曲线ECC算法如ECDSA因其更短的密钥长度和更高的效率正逐渐成为新的标准。但RSA因其广泛的兼容性仍然是许多场景下的实用选择。cryptography对两者都支持这里以RSA为例。4.1 生成RSA密钥对首先服务器端需要生成一对公私钥。from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives import serialization # 生成私钥 private_key rsa.generate_private_key( public_exponent65537, # 标准公钥指数固定用这个值 key_size2048, # 密钥长度2048位是当前最低安全要求推荐4096位用于长期密钥 ) # 提取公钥 public_key private_key.public_key() # 序列化私钥以PEM格式保存并加密 private_pem private_key.private_bytes( encodingserialization.Encoding.PEM, formatserialization.PrivateFormat.PKCS8, encryption_algorithmserialization.BestAvailableEncryption(bmypassword) # 用密码保护私钥 ) with open(private_key.pem, wb) as f: f.write(private_pem) print(私钥已保存到 private_key.pem (受密码保护)) # 序列化公钥以PEM格式保存无需加密 public_pem public_key.public_bytes( encodingserialization.Encoding.PEM, formatserialization.PublicFormat.SubjectPublicKeyInfo ) with open(public_key.pem, wb) as f: f.write(public_pem) print(公钥已保存到 public_key.pem)密钥安全须知私钥等同于你的身份印章必须绝对保密。在生产环境中应存储在安全的硬件模块HSM或至少是加密的密钥存储服务中。上述代码用密码加密了PEM文件但这只是基础保护。公钥可以自由分发。你可以把它放在客户端的代码仓库里、网站上任何需要验证你签名的地方。4.2 对数据进行签名服务器端当服务器准备好要分发的数据比如一个Python脚本文件时用它自己的私钥进行签名。from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import padding import hashlib # 假设这是要分发的数据 data_to_sign b# 这是一个重要的更新脚本 def critical_update(): print(Applying safe and verified update...) # 1. 首先计算数据的哈希值使用SHA-256 # 对于大文件应该分块更新哈希这里是小数据演示 digest hashlib.sha256(data_to_sign).digest() # 2. 使用私钥对哈希值进行签名 # 加载之前保存的受密码保护的私钥 from cryptography.hazmat.primitives.serialization import load_pem_private_key with open(private_key.pem, rb) as f: private_key load_pem_private_key( f.read(), passwordbmypassword, # 提供加密密码 ) signature private_key.sign( data_to_sign, # 可以直接对原始数据签名库内部会处理哈希 padding.PSS( mgfpadding.MGF1(hashes.SHA256()), salt_lengthpadding.PSS.MAX_LENGTH ), hashes.SHA256() # 指定哈希算法 ) print(f生成的签名: {signature.hex()}) # 通常我们会将 {数据 签名} 一起打包发给客户端。 # 为了演示我们简单地把签名保存到文件 with open(update_script.sig, wb) as f: f.write(signature)这里我们使用了PSSProbabilistic Signature Scheme填充方案和SHA-256哈希算法这是目前推荐的安全组合。cryptography库的.sign()方法实际上已经帮我们完成了“先哈希再对哈希值加密签名”的两步过程。4.3 验证签名客户端客户端拿到数据和签名后使用服务器公开的公钥进行验证。from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import padding from cryptography.exceptions import InvalidSignature # 客户端拥有数据 (data_to_sign)签名 (signature)和服务器的公钥 (public_key) # 加载公钥 with open(public_key.pem, rb) as f: public_key serialization.load_pem_public_key(f.read()) # 假设我们从网络收到了数据和签名 received_data data_to_sign # 实际中从网络包解析 with open(update_script.sig, rb) as f: received_signature f.read() try: # 使用公钥验证签名 public_key.verify( received_signature, received_data, padding.PSS( mgfpadding.MGF1(hashes.SHA256()), salt_lengthpadding.PSS.MAX_LENGTH ), hashes.SHA256() ) print(签名验证成功数据完整且来自可信服务器。) # 此时可以安全地执行 received_data 中的脚本 # exec(received_data.decode()) # 注意实际执行代码需极度谨慎应有沙箱等安全措施 except InvalidSignature: print(警告签名验证失败。数据可能被篡改或来源不可信。) # 中止操作记录安全事件验签过程的核心公钥的verify方法会使用相同的参数填充方案、哈希算法对接收到的数据重新计算哈希并用公钥解密收到的签名得到服务器当初计算的哈希值。两者对比一致则通过。如果数据被篡改哈希值会变解密签名得到的哈希值也会不匹配或者签名本身无效从而抛出InvalidSignature异常。实操心得在实际系统中除了签名还应考虑数据的时效性防止重放攻击。常见的做法是在签名的数据中包含一个时间戳或一个递增的序列号并在验证签名后检查这个时间戳/序列号是否在有效范围内。5. 混合加密实践安全分发对称密钥现在我们将对称加密和非对称加密结合起来解决一个经典问题如何安全地将一个对称加密密钥比如一个Fernet密钥发送给另一方这是许多安全通信协议如TLS/SSL的核心思想。场景你服务器需要让许多客户端都能加密它们本地存储的数据但你不想为每个客户端管理不同的对称密钥。你可以让每个客户端生成自己的对称密钥然后用你的公钥加密后发给你。这样只有你能用私钥解密看到这些对称密钥。from cryptography.fernet import Fernet from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives import serialization, hashes import os # --- 客户端操作 --- print( 客户端生成并加密对称密钥 ) # 1. 客户端生成一个用于本地数据加密的Fernet密钥 local_fernet_key Fernet.generate_key() print(f客户端生成的Fernet密钥: {local_fernet_key.decode()}) # 2. 客户端加载服务器的公钥假设已从可信渠道获得 with open(server_public_key.pem, rb) as f: # 这个文件是公开分发的 server_public_key serialization.load_pem_public_key(f.read()) # 3. 使用服务器的RSA公钥加密这个Fernet密钥 # RSA加密有长度限制不能加密过长的数据。Fernet密钥是32字节2048位RSA密钥可以加密。 encrypted_fernet_key server_public_key.encrypt( local_fernet_key, padding.OAEP( # 使用OAEP填充方案比旧的PKCS1v1.5更安全 mgfpadding.MGF1(algorithmhashes.SHA256()), algorithmhashes.SHA256(), labelNone ) ) print(f加密后的Fernet密钥 (发送给服务器): {encrypted_fernet_key.hex()}) # 客户端现在可以将 encrypted_fernet_key 安全地发送给服务器。 # 即使被截获没有服务器私钥也无法解密。 # --- 服务器操作 --- print(\n 服务器接收并解密对称密钥 ) # 4. 服务器收到 encrypted_fernet_key 后用自己的私钥解密 with open(private_key.pem, rb) as f: server_private_key load_pem_private_key(f.read(), passwordbmypassword) decrypted_fernet_key server_private_key.decrypt( encrypted_fernet_key, padding.OAEP( mgfpadding.MGF1(algorithmhashes.SHA256()), algorithmhashes.SHA256(), labelNone ) ) print(f服务器解密得到的Fernet密钥: {decrypted_fernet_key.decode()}) # 验证解密是否正确 assert decrypted_fernet_key local_fernet_key, 密钥解密后不匹配 print(验证成功服务器已安全获取客户端的对称密钥。) # 现在服务器可以将这个 decrypted_fernet_key 安全地存储起来 # 未来如果需要解密该客户端的数据例如在服务端进行数据分析就可以使用它。 # 而客户端则用 local_fernet_key 在本地加密自己的数据。这个模式的优势高效后续客户端本地的大量数据加密使用快速的对称加密AES。安全对称密钥的分发通过安全的非对称加密完成。灵活服务器无需预先知道或生成客户端的密钥。注意事项RSA加密有明文长度限制。对于2048位密钥最多只能加密245字节左右的数据取决于填充方案。因此它只适合加密像对称密钥这样的短数据。如果要加密更长的消息标准的做法是随机生成一个对称密钥会话密钥用RSA加密这个会话密钥然后用会话密钥加密实际的长消息。这本质上就是上面模式的延伸。6. 常见问题、排查技巧与安全最佳实践实录在实际集成cryptography库时你肯定会遇到一些坑。下面是我从多个项目中总结出来的常见问题清单和解决思路。6.1 编码与数据类型错误这是新手最常掉进的坑。问题TypeError: data must be bytes.原因cryptography的加解密、签名操作几乎都要求输入是bytes类型而不是str。解决# 加密字符串前 text 我的秘密 data text.encode(utf-8) # 转换为 bytes encrypted cipher_suite.encrypt(data) # 解密后得到 bytes想转回字符串 decrypted_data cipher_suite.decrypt(encrypted) text_output decrypted_data.decode(utf-8) # 解码为 str技巧养成条件反射看到文本先想.encode()拿到bytes后想.decode()。6.2 密钥管理不当导致的安全漏洞问题1密钥硬编码在源码中并上传到了GitHub。后果一旦仓库公开或泄露密钥直接暴露。攻击者可以用它解密所有数据或伪造签名。最佳实践12-Factor App原则将密钥存储在环境变量中。使用.env文件开发环境配合python-dotenv库但确保.env在.gitignore中。密钥轮换为密钥设置过期时间并建立定期轮换机制。Fernet支持多密钥列表可以平滑过渡。生产环境使用KMS务必使用专业的密钥管理服务。问题2使用弱密钥或不当的密钥生成方式。解决对称密钥如AES必须使用密码学安全的随机数生成器。os.urandom()或cryptography库自己的生成函数如Fernet.generate_key()、AESGCM.generate_key()是安全的。绝对不要自己用random模块或时间戳造密钥。RSA密钥长度至少2048位推荐4096位用于长期密钥。6.3 算法与参数选择陷阱问题该用哪种填充方案该用CBC还是GCM指南对称加密优先选择认证加密模式如AES-GCM或ChaCha20-Poly1305。它们同时提供机密性和完整性。如果使用CBC模式必须结合HMAC来保证完整性这正是Fernet帮你做的否则易受填充预言攻击。RSA签名使用PSS填充而不是旧的PKCS1v1.5填充。PSS安全性更高。RSA加密使用OAEP填充而不是PKCS1v1.5。哈希算法使用SHA-256或SHA-3等强哈希算法避免已破译的MD5、SHA-1。6.4 性能考量与异常处理性能非对称操作RSA加解密、签名验签非常耗时。避免用它加密超过其承载能力的数据如前所述通常只用于加密密钥。对于大批量数据始终使用对称加密。异常处理加解密、验签操作可能因多种原因失败密钥错误、数据篡改、填充错误等。务必使用try...except块进行精细化的异常捕获。from cryptography.fernet import InvalidToken from cryptography.exceptions import InvalidSignature, InvalidKey try: decrypted_data cipher_suite.decrypt(some_token) except InvalidToken: # 处理无效令牌的情况可能是密钥错误、数据损坏、被篡改 log.security_event(解密失败无效令牌) return None except Exception as e: # 捕获其他意外错误 log.error(f解密过程发生未知错误: {e}) return None6.5 版本兼容性与依赖问题问题在不同环境开发机、测试服务器、生产服务器部署时因为cryptography或底层OpenSSL版本不同导致序列化的密钥无法加载或加解密行为不一致。解决使用requirements.txt或Pipfile精确锁定cryptography的版本。密钥序列化时使用标准、兼容性好的格式如PEM。避免使用自定义或私有格式。在持续集成CI流水线中使用与生产环境相同的Docker镜像或系统版本来进行测试提前发现兼容性问题。6.6 一个完整的配置加密示例保护Django的SECRET_KEY让我们看一个贴近实际的应用。Django的SECRET_KEY至关重要但很多项目就明文写在settings.py里。我们可以用Fernet加密它。步骤1生成并导出加密密钥# 在安全的部署服务器上操作 python -c from cryptography.fernet import Fernet; print(Fernet.generate_key().decode()) # 将输出设置为环境变量 ENCRYPTION_KEY步骤2创建加密脚本encrypt_secret.pyimport os from cryptography.fernet import Fernet encryption_key os.environ.get(ENCRYPTION_KEY) if not encryption_key: raise ValueError(请设置 ENCRYPTION_KEY 环境变量) cipher Fernet(encryption_key.encode()) # 你的原始 SECRET_KEY original_secret_key django-insecure-your-very-long-secret-key-here encrypted_secret cipher.encrypt(original_secret_key.encode()) print(f加密后的 SECRET_KEY (可放入环境变量):) print(encrypted_secret.decode())步骤3在Django设置中解密使用# settings.py import os from cryptography.fernet import Fernet, InvalidToken def get_secret_key(): encryption_key os.environ.get(ENCRYPTION_KEY) encrypted_secret os.environ.get(ENCRYPTED_SECRET_KEY) if not encryption_key or not encrypted_secret: # 回退到明文或抛出错误 raise RuntimeError(加密密钥或加密后的密钥未配置) cipher Fernet(encryption_key.encode()) try: return cipher.decrypt(encrypted_secret.encode()).decode() except InvalidToken: # 记录严重安全错误并可能使应用启动失败 raise RuntimeError(解密SECRET_KEY失败请检查ENCRYPTION_KEY) SECRET_KEY get_secret_key()这样你的settings.py和代码仓库里就不再出现明文的SECRET_KEY了。即使有人拿到你的代码没有部署服务器上的ENCRYPTION_KEY也无法获得真实的密钥。安全是一个过程而不是一个状态。从今天开始审视你的Python项目中是否有“裸露”的敏感信息尝试用cryptography库为它们穿上第一层防护衣。从加密一个配置文件开始逐步构建起完整的数据安全实践。