Python 高安全性加密密码容器类

这个类提供了一个高安全性的加密密码容器,用于存储有一定安全级别的密码。使用时务必遵循规范,否则无法保证其安全性。

对数据加密还不是太熟悉,如有任何漏洞欢迎批评指正!

代码

python 复制代码
import ctypes
import mmap
import os
import pickle
import secrets
import sys
import functools
import hashlib
import hmac
from typing import Any, Optional, Callable
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC


class SafeKey:
    def __init__(self, password: str):
        """
        仅能单次使用、高安全性的加密密码容器类
        """
        # 将密码转换为可变字节数组
        password_bytes = bytearray(password.encode('utf-8'))
        
        # 生成随机内部主密钥 (32字节)
        self._internal_master_key = secrets.token_bytes(32)
        
        # 生成随机nonce
        nonce = secrets.token_bytes(12)
        
        # 使用AES-GCM加密密码
        cipher = Cipher(
            algorithms.AES(self._internal_master_key),
            modes.GCM(nonce),
            backend=default_backend()
        )
        encryptor = cipher.encryptor()
        encrypted = encryptor.update(password_bytes) + encryptor.finalize()
        
        # 创建加密数据包并添加HMAC校验
        encrypted_package = nonce + encryptor.tag + encrypted
        hmac_digest = hmac.new(
            self._internal_master_key,
            encrypted_package,
            hashlib.sha3_256
        ).digest()
        self._encrypted_data = hmac_digest + encrypted_package
        
        # 安全清除原始密码
        self._secure_erase(password_bytes)
        del password_bytes
        del password  # 删除引用
        
        # 锁定内部主密钥和加密数据在内存中
        self._locked_master_key = self._lock_in_memory(self._internal_master_key)
        self._locked_encrypted_data = self._lock_in_memory(self._encrypted_data)
        
        # 清除原始密钥引用
        self._secure_erase(self._internal_master_key)
        self._internal_master_key = None
        self._secure_erase(self._encrypted_data)
        self._encrypted_data = None
        
        # 标记对象状态
        self._is_active = True
        self._password_ref = None
        
        # 反调试保护
        self._check_security_environment()
    
    def _lock_in_memory(self, data: bytes) -> mmap.mmap:
        """将数据安全锁定在内存中,防止交换到磁盘"""
        size = len(data)
        
        # 创建共享内存区域
        if sys.platform == 'win32':
            shm = mmap.mmap(-1, size, access=mmap.ACCESS_WRITE)
        else:
            # Unix系统内存保护
            PROT_READ = 1
            PROT_WRITE = 2
            PROT_READWRITE = PROT_READ | PROT_WRITE
            shm = mmap.mmap(-1, size, prot=PROT_READWRITE)
        
        # 复制数据到锁定内存
        shm.write(data)
        
        # 锁定内存防止交换
        if not self._mlock_memory(shm, size):
            # 锁定失败时立即清除并终止
            self._secure_erase(shm)
            shm.close()
            self._terminate_with_error("内存锁定失败")
        
        # 安全清除原始数据
        self._secure_erase(data)
        
        return shm
    
    def _mlock_memory(self, shm: mmap.mmap, size: int) -> bool:
        """锁定内存防止交换到磁盘"""
        try:
            if sys.platform == 'win32':
                # Windows内存锁定
                return ctypes.windll.kernel32.VirtualLock(
                    ctypes.c_void_p(ctypes.addressof(ctypes.c_char.from_buffer(shm))),
                    size
                ) != 0
            else:
                # Unix系统内存锁定
                libc = ctypes.CDLL(None)
                return libc.mlock(
                    ctypes.c_void_p(ctypes.addressof(ctypes.c_char.from_buffer(shm))),
                    size
                ) == 0
        except:
            return False
    
    def _secure_erase(self, buffer) -> None:
        """安全清除内存内容"""
        if buffer is None:
            return
        
        try:
            if isinstance(buffer, (bytes, bytearray)):
                # 多次覆盖内存
                mutable = bytearray(buffer)
                for _ in range(7):
                    for i in range(len(mutable)):
                        mutable[i] = secrets.randbelow(256)
                # 填充零
                for i in range(len(mutable)):
                    mutable[i] = 0
                # 防止编译器优化
                if len(mutable) > 0:
                    ctypes.memset(ctypes.addressof(ctypes.c_char.from_buffer(mutable)), 0, len(mutable))
            elif isinstance(buffer, mmap.mmap):
                # 处理内存映射对象
                buffer.seek(0)
                data = buffer.read()
                mutable = bytearray(data)
                for _ in range(7):
                    for i in range(len(mutable)):
                        mutable[i] = secrets.randbelow(256)
                for i in range(len(mutable)):
                    mutable[i] = 0
                buffer.seek(0)
                buffer.write(mutable)
            elif isinstance(buffer, str):
                # 字符串清除
                mutable = bytearray(buffer.encode('utf-8'))
                for _ in range(7):
                    for i in range(len(mutable)):
                        mutable[i] = secrets.randbelow(256)
                for i in range(len(mutable)):
                    mutable[i] = 0
        except:
            pass
    
    def _check_security_environment(self):
        """检查调试和内存转储"""
        # 检测调试器
        if self._is_debugger_present():
            self._terminate_with_error("检测到调试器 - 安全终止")
        
        # 检测虚拟机
        if self._is_running_in_vm():
            self._terminate_with_error("虚拟机环境不安全 - 安全终止")
    
    def _is_debugger_present(self) -> bool:
        """检测调试器"""
        try:
            # Windows调试器检测
            if sys.platform == 'win32':
                kernel32 = ctypes.windll.kernel32
                return kernel32.IsDebuggerPresent() != 0
            
            # Linux调试器检测
            elif sys.platform.startswith('linux'):
                try:
                    with open('/proc/self/status', 'r') as status_file:
                        for line in status_file:
                            if line.startswith('TracerPid:'):
                                tracer_pid = int(line.split(':')[1].strip())
                                return tracer_pid != 0
                except:
                    pass
                
                # 检查LD_PRELOAD劫持
                if 'LD_PRELOAD' in os.environ:
                    return True
        except:
            pass
        
        return False
    
    def _is_running_in_vm(self) -> bool:
        """检测是否在虚拟机中运行"""
        try:
            # 简单虚拟机检测
            if sys.platform == 'win32':
                import winreg
                with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,
                                    r"SYSTEM\CurrentControlSet\Control\SystemInformation") as key:
                    system_manufacturer = winreg.QueryValueEx(key, "SystemManufacturer")[0]
                    system_product = winreg.QueryValueEx(key, "SystemProductName")[0]
                    vm_indicators = ["VMware", "Virtual", "Xen", "QEMU", "KVM"]
                    return any(
                        indicator in system_manufacturer or indicator in system_product for indicator in vm_indicators)
            
            elif sys.platform.startswith('linux'):
                # 检查DMI信息
                dmi_files = [
                    '/sys/class/dmi/id/product_name',
                    '/sys/class/dmi/id/sys_vendor'
                ]
                for dmi_file in dmi_files:
                    if os.path.exists(dmi_file):
                        with open(dmi_file, 'r') as f:
                            content = f.read().lower()
                            if 'vmware' in content or 'virtual' in content or 'qemu' in content:
                                return True
        except:
            pass
        
        return False
    
    def _terminate_with_error(self, message: str):
        """安全终止程序并显示错误消息"""
        infoget(f"安全错误: {message}",type = "err")
        
        # 尝试安全清除内存
        if hasattr(self, '_locked_master_key'):
            self._secure_erase(self._locked_master_key)
        if hasattr(self, '_locked_encrypted_data'):
            self._secure_erase(self._locked_encrypted_data)
        
        # 立即终止程序
        os._exit(1)
    
    def __enter__(self):
        """进入上下文时获取密码访问对象"""
        if not self._is_active:
            raise RuntimeError("SafeKey 已销毁")
        
        # 返回自身而不是直接解密密码
        return self
    
    def get_password(self) -> bytearray:
        """安全获取密码(使用后立即清除)"""
        if not self._is_active:
            raise RuntimeError("SafeKey 已销毁")
        
        # 1. 从锁定内存中获取加密数据
        self._locked_encrypted_data.seek(0)
        full_package = self._locked_encrypted_data.read()
        
        # 验证数据包长度
        if len(full_package) < 32 + 12 + 16:  # HMAC(32) + nonce(12) + tag(16)
            self._terminate_with_error("加密数据包损坏")
        
        # 2. 分离HMAC和加密数据
        hmac_digest = full_package[:32]
        encrypted_package = full_package[32:]
        
        # 3. 从锁定内存中获取主密钥
        self._locked_master_key.seek(0)
        master_key = self._locked_master_key.read()
        
        # 4. 验证HMAC
        expected_hmac = hmac.new(
            master_key,
            encrypted_package,
            hashlib.sha3_256
        ).digest()
        
        if not hmac.compare_digest(hmac_digest, expected_hmac):
            self._terminate_with_error("加密数据完整性校验失败")
        
        # 5. 解包加密数据
        nonce = encrypted_package[:12]
        tag = encrypted_package[12:28]
        ciphertext = encrypted_package[28:]
        
        # 6. 使用AES-GCM解密
        cipher = Cipher(
            algorithms.AES(master_key),
            modes.GCM(nonce, tag),
            backend=default_backend()
        )
        decryptor = cipher.decryptor()
        decrypted = decryptor.update(ciphertext) + decryptor.finalize()
        
        # 7. 创建可变密码副本
        password_bytes = bytearray(decrypted)
        
        # 8. 安全清除临时数据
        self._secure_erase(full_package)
        self._secure_erase(master_key)
        self._secure_erase(decrypted)
        
        return password_bytes
    
    def use_password(self, callback: callable) -> None:
        """安全使用密码"""
        password_bytes = self.get_password()
        try:
            # 将字节数组转换为字符串
            password_str = password_bytes.decode('utf-8') if callback.__annotations__.get('password',
                                                                                          None) is str else password_bytes
            callback(password_str)
        finally:
            # 确保密码被清除
            self._secure_erase(password_bytes)
            if 'password_str' in locals():
                self._secure_erase(bytearray(password_str.encode('utf-8')))
    
    def __exit__(self, exc_type, exc_value, traceback):
        """退出上下文时销毁所有敏感数据"""
        # 安全清除并销毁密码引用
        if self._password_ref:
            self._secure_erase(self._password_ref)
            try:
                self._password_ref.close()
            except:
                pass
            self._password_ref = None
        
        # 安全清除并销毁主密钥
        if self._locked_master_key:
            self._secure_erase(self._locked_master_key)
            try:
                self._locked_master_key.close()
            except:
                pass
            self._locked_master_key = None
        
        # 安全清除并销毁加密数据
        if self._locked_encrypted_data:
            self._secure_erase(self._locked_encrypted_data)
            try:
                self._locked_encrypted_data.close()
            except:
                pass
            self._locked_encrypted_data = None
        
        # 标记对象为已销毁
        self._is_active = False
    
    def __del__(self):
        """析构时确保内存安全清除"""
        if hasattr(self, '_is_active') and self._is_active:
            self.__exit__(None, None, None)

使用方法

python 复制代码
import getpass
with SafeKey(getpass.getpass()) as sk: # input: a1!B2$c3*D4&e5_A+Strong%Password#F6(g7|H8.i9?j0
    xxx.decrypt(sk.get_password()) # 不推荐    
    sk.use_password(xxx.decrypt)   # 更安全
# 退出上下文即彻底销毁密码
# print(sk.get_password()) # 报错

# password = input("Password:") # 禁止保存到变量或明文输入
# sk = SafeKey(password)        # 禁止不使用上下文管理器
# print(sk.get_password())      # 禁止在控制台或任何地方泄露密码

以下内容由AI辅助生成,仅作参考

安全特性评估

安全维度 实现机制 安全等级
数据加密 AES-256-GCM + HMAC-SHA3-256 ★★★
密钥管理 内部随机生成主密钥 + 单次使用 ★★★
内存保护 mlock/VirtualLock + 7次覆盖清除 ★★★
暴露控制 按需解密 + 回调自动清除 ★★★
完整性保护 HMAC-SHA3-256 数据校验 ★★★
反调试保护 调试器检测 + 安全终止 ★★☆
生命周期管理 上下文绑定 + 自动销毁 ★★★
数据类型安全 可变字节数组 + 主动清除 ★★★

安全边界分析

1. 物理安全边界

内存锁定:防止敏感数据交换到磁盘

冷启动防护:多次覆盖清除抵御内存冷冻攻击

硬件依赖:不依赖特定硬件,通用安全实现
2. 运行时安全边界

反调试保护:检测并阻止调试器附加

时间窗口控制:密码仅在需要时解密

完整性校验:HMAC防止数据篡改

威胁模型防护

威胁 防护措施 有效性
内存扫描 内存锁定 + 加密 ★★★
冷启动攻击 多次覆盖清除 ★★☆
调试分析 反调试检测 + 自毁 ★★☆
数据篡改 HMAC完整性校验 ★★★
密码泄露 单次使用 + 自动清除 ★★★
进程转储 内存加密 + 锁定 ★★☆

局限性说明

1. 系统级限制:

依赖操作系统内存保护机制

无法防御内核级攻击

硬件漏洞(如Meltdown/Spectre)可能绕过保护
2. 实现限制:

Python解释器可能的内存管理特性

垃圾回收机制可能延迟内存释放

无法完全控制CPU缓存
3. 使用场景限制:

单机保护,不适用于分布式系统

不适合长期存储(设计为短期使用)

性能开销(适用于敏感操作,不适用高频访问)

安全认证对标

1.FIPS 140-2 Level 3: 物理篡改检测(自毁);密钥管理;加密算法实现
2.PCI DSS: 强加密保护存储数据;安全密钥管理;访问控制
3.GDPR: 数据最小化;存储限制;完整性与机密性
**4.NIST SP 800-131A:**过渡到强加密;密钥管理要求;随机数生成


制作不易。感谢大家的支持!