如何保证在运行时解密的文件被清除?

时间:2008-11-10 16:06:04

标签: c++ c encryption filesystems

使用C或C ++,在将文件解密到磁盘后 - 如果应用程序崩溃或系统关闭且无法正常清理,我如何保证将其删除?在Windows和Linux上使用C或C ++?

13 个答案:

答案 0 :(得分:7)

不幸的是,没有100%万无一失的方法可以确保在系统崩溃的情况下删除该文件。想想如果用户只是在文件在磁盘上时拔出插头会发生什么。没有任何异常处理可以保护您免受那种(最糟糕的)情况的影响。

您可以做的最好的事情就是首先将解密文件写入磁盘。如果文件以加密和解密形式存在,那么这就是安全性方面的一个弱点。

您可以做的第二件事是使用Brian的结构化异常处理建议,以确保临时文件得到清理。这不会保护你免受所有可能性的影响,但它会有很长的路要走。

最后,我建议您在启动应用程序时检查临时解密文件。这将允许您在应用程序完成系统崩溃后进行清理。在任何的时间内使用这些文件并不理想,但至少可以让你尽快摆脱它们。

答案 1 :(得分:5)

根本不要将解密的文件写入磁盘。

如果关闭系统,文件仍在磁盘上,则可以访问磁盘,因此可以访问该文件。

例外情况是使用加密文件系统,但这不受程序控制。

答案 2 :(得分:4)

尽量完全避免:

如果文件是敏感的,最好的办法是首先不要以解密格式将其写入磁盘。

防止崩溃:结构化异常处理:

但是,您可以添加结构化异常处理以捕获任何崩溃。

__try and __except

如果拔掉插头怎么办?

有一种方法可以防止这种情况......

如果您使用的是Windows,则可以使用MoveFileEx和目标为NULL的MOVEFILE_DELAY_UNTIL_REBOOT选项在下次启动时删除该文件。这将防止意外的计算机关闭与未删除的文件。您还可以确保您拥有此文件的独占打开句柄(不指定共享权限,例如FILE_SHARE_READ并使用CreateFile打开它)。这样,没有人能够从中读取。

避免此问题的其他方法: 所有这些都不是在磁盘上拥有解密文件的借口,但是:

  • 您还可以考虑通过\\?\的文件语法写入大于MAX_PATH的文件。这将确保Windows资源管理器无法浏览该文件。

  • 您应该将文件设置为具有临时属性

  • 您应该将文件设置为隐藏属性

答案 3 :(得分:4)

我不知道这是否适用于Windows,但在Linux上,假设您只需要一个进程来访问解密文件,您可以打开该文件,然后调用unlink()来删除该文件。只要进程保持打开状态,该文件将继续存在,但当它关闭或进程终止时,该文件将不再可访问。

当然文件的内容仍然在磁盘上,所以你真的需要的不仅仅是删除它,而是将内容归零。有没有理由说解密文件需要在磁盘上(大小?)。更好的方法是将解密后的版本保留在内存中,最好标记为不可篡改,因此它永远不会碰到磁盘。

答案 4 :(得分:2)

在C中(我也假设,在C ++中),只要程序没有崩溃,你就可以注册一个atexit()处理程序来进行清理。请避免使用_exit()_Exit(),因为这些会绕过atexit()处理程序。

正如其他人指出的那样,最好避免将解密数据写入磁盘。简单地使用unlink()(或等效物)是不够的;您需要在原始数据上重写其他一些数据。而且记录的文件系统非常困难。

答案 5 :(得分:1)

一个过程无法保护或观察自己。您唯一的可能性是启动第二个进程作为一种监视程序,它定期检查解密其他进程的运行状况。如果其他进程崩溃,监视程序将注意并删除该文件本身。

您可以使用hearth-beats(定期轮询其他进程以查看它是否仍处于活动状态),或使用从其他进程本身发送的中断来执行此操作,如果崩溃,则会触发超时。

例如,您可以使用套接字在监视程序和应用程序之间建立连接。

很明显,您需要一些锁定机制来防止交换到pagefile / swap-partition。在Posix系统上,这可以通过m(un)lock* family of functions完成。

答案 6 :(得分:1)

查看tmpfile()。

它是BSD UNIX的一部分,不确定它是否是标准的 但它会创建一个临时文件并自动取消链接,以便在关闭时删除它。

写入文件系统(即使是暂时的)是不安全的 只有你真的必须这样做才能做到。

您可以选择创建内存中的文件系统 从来没有使用过我自己,所以没有推荐,但快速谷歌找到了一些。

答案 7 :(得分:1)

删除文件时出现问题。它并没有真正消失。 当您从硬盘驱动器上删除文件(不包括回收站)时,文件并没有真正消失。只删除指向该文件的指针。

曾经看过那些间谍电影,他们会覆盖硬盘6,8,24次,这就是他们知道它干净的方式......好吧他们这样做是有原因的。 我会尽一切努力不存储文件的解密数据。或者如果必须,请填写少量数据。甚至,脱节的数据。

如果你必须,那么他们尝试捕捉应该保护你一点..没有什么可以防止停电但是。

祝你好运。

答案 8 :(得分:1)

在C ++中,你应该使用RAII策略:

class Clean_Up_File {
    std::string filename_;
    public Clean_Up_File(std::string filename) { ... } //open/create file
    public ~Clean_Up_File() { ... } //delete file
}

int main()
{
    Clean_Up_File file_will_be_deleted_on_program_exit("my_file.txt");
}

RAII有助于自动化大量清理工作。您只需在堆栈上创建一个对象,并让该对象在其生命周期结束时进行清理(在析构函数中,当对象超出范围时将调用该析构函数)。 ScopeGuard甚至让它变得更容易。

但是,正如其他人所说,这只适用于“正常”情况。如果用户拔出计算机,则无法保证该文件将被删除。并且可以取消删除该文件(即使在UNIX上也可以“grep the harddrive”)。


此外,正如评论中所指出的,在某些情况下,对象不会超出范围(例如,std::exit(int)函数退出程序而不离开当前范围),因此RAII不会在这些情况下工作。就个人而言,我从不调用std::exit(int),而是抛出异常(将展开堆栈并调用析构函数;我认为这是“异常退出”)或者从main()返回错误代码(这将是调用析构函数,我也认为是“异常退出”)。 IIRC,发送SIGKILL也不会调用析构函数,并且SIGKILL无法被捕获,所以你也运气不好。

答案 9 :(得分:0)

这是一个棘手的话题。通常,如果可以避免,则不希望将解密文件写入磁盘。但是将它们保存在内存中并不总是保证它们不会作为页面文件的一部分写入磁盘或其他方式。

我很久以前就读过这篇文章了,我记得Windows和Linux之间存在一些区别,因为人们可以保证内存页不能写入磁盘,而且不能写入磁盘。但我记不太清楚了。

如果您想进行尽职调查,可以查看该主题并阅读相关内容。这一切都取决于您的威胁模型以及您愿意保护的内容。毕竟,你可以使用压缩空气冷却RAM并从中取出加密密钥(实际上是新的Christian Slater间谍节目,我自己最糟糕的敌人 - 我认为这是最前沿,准确,最好的计算机媒体中的安全技术)

答案 10 :(得分:0)

在Linux / Unix上,创建文件后立即使用unlink。一旦程序关闭文件描述符或退出,该文件将被删除。

更好的是,即使整个系统崩溃,该文件也会被删除 - 因为一旦你取消链接它就会被删除。

当然,数据不会从磁盘中物理删除,因此它仍可用于黑客攻击。

答案 11 :(得分:0)

请记住,计算机可以随时关闭。然后,你不喜欢的人可以使用Linux Live CD启动,并在不改变任何内容的情况下检查所需的任何详细级别的磁盘。没有将明文写入磁盘的系统可以抵御这种攻击,并且它们并不难做到。

您可以设置一个函数,该函数将重复覆盖1和0的文件,最好注入一些随机性,并将其设置为在程序结束时或退出时运行。如果没有硬件或软件故障,电源故障或其他中断,并且文件系统仅写入其声称正在使用的扇区(例如,日志文件系统,可能会将部分文件留在其他位置),这将起作用)。

因此,如果您需要安全性,则需要确保没有写出明文,这也意味着它无法写入交换空间或等效文件。了解如何在您正在编写的所有平台上将内存标记为不可修复。确保解密密钥等的处理方式与明文相同:在任何情况下都不要写入磁盘,并保存在不可挽回的内存中。

然后,你的系统应该能够安全抵御敌人闯入,打断你,并在关机之前冻结你的RAM芯片的攻击,这样他们就不会在转移检查之前丢失内容。或当局要求您的钥匙,合法(在此查看您当地的法律)或非法。

故事的道德:真正的安全很难。

答案 12 :(得分:0)

我要实现的方法是流解密 - 所以内存中唯一的部分是在读取过程中使用数据时解密的部分。这是管道图:

这将是一个流式实现,因此内存中唯一的数据是我在任何给定点在应用程序中消耗的数据。这使得一些事情变得棘手 - 考虑到许多传统的文件技巧不再可用,但由于实现将基于流,我仍然能够寻找文件的不同点,这些点将被转换为加密流以解密不同的部分。

基本上,它将一次加密文件的块 - 所以如果我试图寻找某一点,它将解密该块以进行读取。当我读过一个块时,它会解密下一个块并释放前一个块(在加密流中)。

此实现不要求我解密到文件或内存,并且与其他流使用者和提供者(fstream)兼容。

这是我的'计划'。我以前没有用fstream做过这种类型的工作,一旦我准备好了,我就可能会发布一个问题。

感谢所有其他答案 - 这是非常有用的。