明确从内存中删除敏感数据?

10 memory security

最近来自维基解密的漏洞让CIA做了以下事情:

只要不再需要纯文本格式的数据,就要从内存中明确删除敏感数据(加密密钥,原始收集数据,shellcode,上传的模块等).

不要依赖操作系统在执行终止时执行此操作.

我是*nix世界的开发者; 我认为这仅仅是改变变量的值(确保我没有通过值;而是通过引用); 所以,如果它是一个100个字符的字符串; 写0是101个字符.这真的很简单吗?如果不是,为什么以及应该做什么呢?

注意:有类似的问题要求这个; 但它在C#和Windows世界中.所以,我不认为这个问题是重复的.

LSe*_*rni 6

我是*nix世界的开发者; 我认为这仅仅是改变变量的值(确保我没有通过值;而是通过引用); 所以,如果它是一个100个字符的字符串; 写0是101个字符.这真的很简单吗?如果不是,为什么以及应该做什么呢?

应该是这么简单.细节决定成败.

  • 内存分配函数,例如realloc,不能保证单独留下内存(你不应该依赖于它们以某种方式进行操作 - 另请参阅此问题).如果你分配1K内存,然后将其重新分配到10K,你的原始K 可能仍然在其他地方,包含其敏感的有效负载.然后它可以由另一个不敏感的变量或缓冲区分配,或者不通过新的变量,可以访问部分或全部旧内容,就像在某些文件系统上的松弛空间一样.
  • 手动归零内存(以及大多数编译器,bzeromemset计算为手动循环)可能会轻微优化,特别是如果你将局部变量归零("bug" - 实际上是一个功能,有解决方法).
  • 某些函数可能在本地缓冲区或内存中留下"痕迹",它们会分配和释放.
  • 在某些语言和框架中,数据的整个部分可能最终被移动(例如在所谓的"垃圾收集"期间,如@gene所注意到的).您可以告诉GC不要处理您的敏感区域或以其他方式"固定"它,如果是这样,必须这样做.否则,数据可能会以多个部分副本结束.
  • 信息可能已经通过并留下了您不知道的痕迹(例如:通过网络发送的密码可能会留在网络库读取缓冲区中).
  • 实时内存可能会被换出到磁盘.

realloc做它的事情的例子.内存被部分重写,并且对于某些库,如果"a"不是唯一分配的区域,那么这只会"工作"(因此你需要声明c并在a之后立即分配一些东西,这样a不是最后一个对象并且离开自由成长):

int main() {
        char *a;
        char *b;
        a = malloc(1024);
        strcpy(a, "Hello");
        strcpy(a + 200, "world");
        printf("a at %08ld is %s...%s\n", a, a, a + 200);
        b = realloc(a, 10240);
        strcpy(b, "Hey!");
        printf("a at %08ld is %s...%s, b at %08ld is %s\n", a, a, a + 200, b, b);
        return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

 a at 19828752 is Hello...world
 a at 19828752 is 8????...world, b at 19830832 is Hey!
Run Code Online (Sandbox Code Playgroud)

所以地址a的内存被部分重写 - "Hello"丢失,"world"仍然存在(以及b + 200).

所以你需要自己处理敏感区域的重新分配; 更好的是,在程序启动时预先分配它.然后,告诉操作系统必须永远不要将敏感的内存区域交换到磁盘.然后你需要以编译器不能干扰的方式将其归零.而且你需要使用一个低级别的语言,你肯定不会自己做的事情:一个简单的字符串连接可以产生两个或三个数据副本 - 我相当肯定它发生在PHP 5.2中.

几年前我给自己写了一个小型图书馆 - 还valgrind没有 - 受史蒂夫马奎尔的编写固体代码的启发,除了覆盖各种内存和字符串函数之外,我最终覆盖了内存,然后计算了覆盖缓冲区的校验和.这不是为了安全,我用它来跟踪缓冲区上/下流,双重释放,使用释放的内存 - 这种事情.

然后你需要确保你的失败者工作 - 例如,如果程序中止会发生什么?或许才有可能使之中止的目的是什么?

您需要深入实施防御,并始终考虑尽可能少地保留信息 - 例如在计算期间清除中间缓冲区而不是等待并在最后一次释放整个批次,或者只是退出计划时; 尽可能保持哈希而不是密码; 等等.

当然,这一切都取决于信息的敏感程度以及攻击面可能什么(强制xkcd参考:这里).用memtest86图像重新启动PC 可能是一个可行的选择.想象一下双启动计算机,其中memtest86设置为测试内存并将PC作为默认启动选项关闭.当你想关闭系统时...你重新启动它.PC将重新启动,默认情况下进入memtest86,在关闭电源之前,它将开始用0和1的行军填充所有可用的RAM.祝你好运冷冻引导从信息.

  • 你忘记了一个:在使用例如世代垃圾收集的语言中,收集器可以在程序运行时将数据复制到新位置,这对它是不可见的.副本留在数据的上一代中.能够在竞技场中快照的坏人可以看到程序执行历史的很大一部分. (2认同)

jos*_*ley 5

完成后立即清除密码(密码,密钥等)是相当标准的做法.困难在于处理可能妨碍您的语言和平台功能.

例如,如果C++编译器确定在写入后未读取数据,则可以优化调用memset.或者操作系统可能已将内存分页到磁盘,从而可能以这种方式保留数据.