You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python代码安全加固:AES加密Token存储漏洞排查与单文件访问限制实现

问题背景

我正在开发涉及网络通信的高安全等级项目,基于pycrypto库编写了Token存储类,实现Token的AES加密、base64编解码及逆向解密功能,当前实现流程如下:

  • 配置AES加密密钥
  • 初始化Token对象
  • 执行加密操作

需要排查当前实现的安全漏洞,同时解决技术问题:如何实现类、方法、变量仅能在单个文件内被访问?

现有实现代码如下:

from base64  import b64encode, b64decode
from Crypto.Cipher import AES
 
import sys
import math
 
class Token:
 
    print("[cobalt] setting up encrytion keys...")
    print("[cobalt] reading encryption key file")
 
    key = open('/usr/share/doc/cobalt/encryption', 'r')
    lines = key.readlines()[0].split(':')
 
    print("[cobalt] read values, writing private constants...")
    __COMMON_ENCRYPTION_KEY = lines[0].strip()
    __COMMON_AES_IV16 = lines[1].strip()
 
    del lines
    key.close()
 
    print("[cobalt] wrote constants, closing file stream...")
    print("[cobalt] setted up encryption keys, initializing token object...")
 
    def __init__(self, token:str):
        self._token = token
        self._encrypted = self.__encrypt()
 
        print("[cobalt] token object initialized")
 
    def __get_common(self):
        print("[cobalt] getting common AES...")
        try:
            return AES.new(Token.__COMMON_ENCRYPTION_KEY, AES.MODE_CBC, Token.__COMMON_AES_IV16)
        except ValueError as e:
            print(f"[cobalt]\033[1;31m error:\033[m {e}")
            sys.exit(1)
 
    def __encrypt(self) -> str:
        print("[cobalt] encrypting token...")
 
        common_cipher = self.__get_common(); print(f"[cobalt] got common AES: {common_cipher}")
        token_len = len(self._token); print(f"[cobalt] got token length: {token_len}")
 
        # padded token with next multiple of 16
        padded_token = self._token.rjust(16 * math.ceil(token_len / 16)); print("[cobalt] got padded token")
        raw_encrypted_token = common_cipher.encrypt(padded_token); print("[cobalt] encrypted, encoding...\n")
 
        return b64encode(raw_encrypted_token).decode('utf-8')
 
    def _decrypt(self) -> str:
        print("[cobalt] decrypting token...")
 
        common_cipher = self.__get_common(); print(f"[cobalt] got common AES: {common_cipher}")
        raw_token = b64decode(self._encrypted); print("[cobalt] decoded raw token")
        decrypted_wpadding = common_cipher.decrypt(raw_token); print("[cobalt] decrypted token\n")
 
        return decrypted_wpadding.decode('utf-8').strip()
现有代码安全漏洞排查
  • 依赖库安全风险:当前使用的pycrypto库已停止维护多年,存在多个未修复的公开安全漏洞,应替换为目前社区维护活跃的兼容分支pycryptodome。
  • 密钥加载逻辑风险:
    • 文件读取操作直接写在类定义顶层,模块被导入时就会立刻执行IO操作,没有做异常捕获,一旦密钥文件不存在、权限不足、格式不符合预期,会直接抛出未处理异常导致导入失败,还可能泄露服务器文件路径信息。
    • 密钥文件路径硬编码,部署灵活性差,若文件权限配置不当,低权限进程可直接读取文件窃取加密密钥。
    • AES-CBC模式使用固定IV(初始化向量),会导致相同明文永远生成相同密文前缀,攻击者可通过密文比对直接推断明文内容,完全丧失CBC模式的语义安全能力。
  • 加密填充逻辑错误:使用rjust在明文左侧补空格做对齐填充,不符合AES要求的PKCS#7标准填充规范;解密时直接用strip()删除首尾所有空白字符,会破坏明文首尾本身携带的空格内容,且当明文长度恰好为16字节整数倍时,填充逻辑会多补16字节空格,导致解密后长度异常。
  • 访问控制逻辑失效:双下划线开头的类属性/方法只是Python的名称改写机制,设计目的是避免子类继承时的命名冲突,并非真正的私有控制,外部代码可通过_Token__COMMON_ENCRYPTION_KEY这类形式直接读取密钥、调用内部方法,没有实际防护能力。
  • 运行风险:加密组件初始化失败时直接调用sys.exit(1)终止整个进程,在服务端场景下会导致服务整体异常退出;代码中大量打印调试日志,甚至输出加密器对象信息,生产环境下会泄露内部运行逻辑,给攻击者提供信息。
Python单文件访问限制实现方案

Python没有语法层面的强制私有访问控制,所有访问限制均为约定性质的软限制,要实现类、方法、变量仅在当前文件内可访问的效果,可组合以下方案:

  • 遵循命名约定:所有不希望外部访问的模块级变量、函数、类属性,统一使用单下划线_开头。Python默认的导入机制在执行from 模块 import *时,不会导入单下划线开头的名称,这是Python社区通用的“内部实现,外部禁止访问”的约定标识。
  • 配置模块导出白名单:在文件顶部定义__all__列表,列表内仅写入允许外部导入的类、函数、变量名称,外部执行from 模块 import *时,只能获取到列表内声明的内容,其余内部实现不会被导出。示例配置:
    # 仅允许外部导入Token类,其余内部逻辑均不对外暴露
    __all__ = ["Token"]
    
  • 敏感逻辑不绑定到类/实例属性:将密钥读取、核心加密逻辑实现为单下划线开头的模块级内部函数,类内部调用这些函数完成操作,不要将密钥、加密器实例这类敏感值绑定为类属性或实例属性,减少外部直接读取敏感信息的路径。
  • 注意:不要依赖双下划线开头的名称改写做访问控制,该机制无法阻止外部代码访问内部属性,仅用于解决继承命名冲突问题。
代码修正方向
  • 替换pycrypto依赖为pycryptodome,导入接口保持兼容。
  • 将密钥读取逻辑移到模块级内部函数中,增加完整的异常捕获,不要在类定义顶层直接执行IO操作。
  • 废弃固定IV逻辑,每次加密时随机生成16字节IV,将IV和密文拼接后再做base64编码,解密时先拆分出前16字节作为IV再执行解密操作。
  • 填充逻辑替换为标准PKCS#7填充,解密时按照填充规则移除填充字符,不要用strip()粗暴删除首尾空白。
  • 移除生产环境的调试打印,组件初始化失败时抛出明确的业务异常,由上层逻辑处理,不要直接终止进程。
  • 配置__all__导出白名单,所有内部实现统一加单下划线前缀标识。

内容的提问来源于stack exchange,提问作者vicsafe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 03:06:10