敏感数据不可被系统外界感知
1.2 ID_secretLeak
敏感数据出入软件系统时需采用有效的保护措施。
示例:
void foo(User* u) {
log("username: %s, password: %s", u->name, u->pw); // Non-compliant
}
显然,将敏感数据直接输出到界面、日志或其他外界可感知的介质中是不安全的,需避免敏感数据的有意外传,除此之外,还需要落实具体的保护措施。
保护措施包括但不限于:
- 避免用明文或弱加密方式传输敏感数据
- 避免敏感数据从内存交换到外存
- 避免敏感数据写入内存转储文件
- 应具备反调试机制,使外界无法获得程序的内部数据
- 应具备反注入机制,使外界无法篡改程序的行为
下面以 Windows 平台为例,给出阻止敏感数据从内存交换到外存的示例:
class SecretBuf {
size_t len = 0;
unsigned char* buf = nullptr;
public:
SecretBuf(size_t size) {
auto* tmp = (unsigned char*)VirtualAlloc(
0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE
);
if (VirtualLock(tmp, size)) { // The key point
buf = tmp;
len = size;
} else {
VirtualFree(tmp, 0, MEM_RELEASE);
}
}
~SecretBuf() {
SecureZeroMemory(buf, len); // Clear the secret content
VirtualUnlock(buf, len);
VirtualFree(buf, 0, MEM_RELEASE);
len = 0;
buf = nullptr;
}
size_t size() const { return len; }
unsigned char* ptr() { return buf; }
const unsigned char* ptr() const { return buf; }
};
例中 SecretBuf 是一个缓冲区类,其申请的内存会被锁定在物理内存中,不会与外存交换,可在一定程度上防止其他进程的恶意嗅探,保障缓冲区内数据的安全。SecretBuf 在构造函数中通过 VirtualLock 锁定物理内存,在析构函数中通过 VirtualUnlock 解除锁定,解锁之前有必要清除数据,否则解锁之后残留数据仍有可能被交换到外存,进一步可参见 ID_unsafeCleanup。
SecretBuf 的使用方法如下:
void foo() {
SecretBuf buf(256);
if (buf.ptr()) {
.... // Do something secret using buf.ptr()
} else {
.... // Handle memory error
}
}
在 Linux 等系统中可参见如下有相似功能的接口:
int mlock(const void* addr, size_t len); // In <sys/mman.h>
int munlock(const void* addr, size_t len);
int mlockall(int flags);
int munlockall(void);