本文目录导读:

- 使用内存锁定(Memory Locking)
- 使用安全内存分配库
- 禁用交换(Swap)
- 加密交换空间
- 使用可信执行环境(TEE)
- 进程级隔离与敏感数据最小化
- 使用内核特性(Linux)
- 配置系统全局行为
- 编程最佳实践
- 局限性提醒
- 总结建议
防止密钥被操作系统交换到磁盘(即避免敏感数据写入交换空间/虚拟内存)是信息安全中的一项重要措施,以下是一些常见且有效的技术手段:
使用内存锁定(Memory Locking)
- 原理:通过系统调用将进程的物理内存页锁定在RAM中,强制操作系统不将其交换到磁盘。
- 实现方式:
- Linux:使用
mlock()/mlockall()系统调用,OpenSSL库中的CRYPTO_secure_malloc就是基于此。 - Windows:使用
VirtualLock()或SetProcessWorkingSetSize()函数。 - 编程注意:需以root/管理员权限运行,因为内存锁定通常需要
CAP_IPC_LOCK权限。
- Linux:使用
- 注意事项:
- 不要锁定过多内存,以免影响系统其他进程。
- 锁定内存后,进程退出前需显式释放。
使用安全内存分配库
- 许多加密库提供了专门的安全内存管理函数,
- libsodium:
sodium_malloc()和sodium_mlock()。 - OpenSSL:
OPENSSL_secure_malloc()(需在初始化时启用)。 - Crypto++:
SecBlock模板类。
- libsodium:
- 这些函数会自动执行内存锁定,并在释放前用零覆盖敏感数据。
禁用交换(Swap)
- 临时关闭:
swapoff -a(需root权限),关闭后,系统不再使用任何交换空间。 - 永久关闭:在
/etc/fstab中注释掉swap分区行。 - 风险:不适合内存有限的系统,可能导致OOM(内存耗尽)崩溃。
加密交换空间
- 原理:即使密钥被交换到磁盘,由于交换空间是加密的,攻击者无法直接读取明文数据。
- Linux实现:
- 使用LUKS加密的swap分区(如
cryptsetup)。 - 或使用
swap文件并基于随机密钥加密(如dm-crypt+tmpfs)。
- 使用LUKS加密的swap分区(如
- 自动挂载:配置
/etc/crypttab和/etc/initramfs-tools/scripts/local-premount/。
使用可信执行环境(TEE)
- 硬件级隔离:将密钥和敏感操作放在Intel SGX、AMD SEV或ARM TrustZone等安全的飞地中。
- 优势:操作系统侧无法访问飞地内的内存,自然也无法交换出去。
- 缺点:需要特制硬件和软件适配。
进程级隔离与敏感数据最小化
- 分离敏感操作:将处理密钥的代码放在一个独立的、短生命周期的子进程中。
- 使用
seccomp:限制子进程的系统调用,禁止mlock以外的内存管理操作。 - 及时清除:密钥使用后立即用随机数据覆盖(如
memset_s或explicit_bzero),避免残留在内存中。
使用内核特性(Linux)
madvise()的MADV_DONTDUMP:标记内存区域不参与核心转储(避免密钥被写入dump文件)。MADV_WIPEONFORK:fork后自动清除内存内容(防止子进程泄露)。- 注意:这些只能阻止特定场景(core dump、fork),无法阻止常规swap。
配置系统全局行为
- 降低swappiness:
sysctl vm.swappiness=1或更低,减少系统主动使用swap的倾向。 - 增加RAM:配备足够物理内存,使系统几乎不需要swap。
编程最佳实践
- 避免使用堆分配:将密钥放在栈上的
volatile局部变量中(栈内存通常不会被交换,但并非绝对安全)。 - 使用
secure_allocator(C++17):std::pmr::unsynchronized_pool_resource+ 自定义mlock。 - 显式清零:释放前用
memset_s(防止编译器优化掉)。
局限性提醒
- 没有绝对的安全:即使锁定了内存,如果系统发生挂起/休眠仍可能被写入磁盘(hibernate image)。
- 休眠(hibernation):需要单独处理,例如加密休眠分区或禁用休眠。
总结建议
- 最低成本方案:使用支持
mlock的加密库(如libsodium),并加密交换空间。 - 高安全场景:结合内存锁定 + TEE + 禁用交换 + 加密交换分区。
- 日常开发:至少做到:敏感数据在堆上时用
mlock,用完后立即用零覆盖,并避免使用堆存储长期密钥。
请根据你的操作系统、编程语言和安全等级选择组合策略。注意:在共享主机或容器中,还需考虑其他进程可能通过 /proc/[pid]/mem 读取你的锁定内存(需限制 /proc 权限)。