这个类提供了一个高安全性的加密密码容器,用于存储有一定安全级别的密码。使用时务必遵循规范,否则无法保证其安全性。
对数据加密还不是太熟悉,如有任何漏洞欢迎批评指正!
代码
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:**过渡到强加密;密钥管理要求;随机数生成
制作不易。感谢大家的支持!