在 PHP 中从物理内存中擦除变量的值


Wiping out value of a variable from physical memory in PHP

一般的问题是:在PHP中取消设置字符串变量后,是否可以从物理内存中删除该变量的值?

问题源于安全标准的某些要求(在处理某些重要数据时,不应该将数据从内存转储到磁盘)。根据"内存是否加密?"主题,没有很好的方法来加密内存中的数据。

因此,在PHP中取消设置字符串变量时,您不能确定内存中的数据已被覆盖。关于将新值设置为变量的相同故事。

因此,我很感兴趣是否可以在不更改方法的核心代码的情况下从内存中清除变量值unset

首先,我不确定擦除字符串是否满足您描述的安全要求,因为理论上仍然可以在擦除字符串之前转储内存。但这无论如何都不可能满足,因为如果没有内存中的数据,就无法处理数据。

无论如何,如果你想确保字符串被擦除,我认为在 PHP 中这样做的唯一方法是遍历字符串并修改每个字符:请记住,内存的内容在被覆盖之前不会消失,即使您没有对变量的引用并且 PHP GC 已经运行。

我相信这会起作用:

for( $i=0; $i < strlen($str); $i++ )
    $str[$i] = 'x';

tl;dr:在彻底测试了许多组合之后,似乎覆盖每个字符的 for 循环会清除/更改内存中的字符串内容。只是要小心初始内容的来源,它可能仍然存在于这些变量中!请参阅下面的更新

用 PHP 5.6 做了一些测试:

<?php
     $cc = 'AAAABBBBCCCCDDDD';
     #v1:  unset($cc);
     #v2:  $cc=null;
     #v3:  for( $i=0; $i < strlen($cc); $i++ ) $cc[$i] = 'x';
     sleep(500); // enough time for taking script pid and run the memory dump
?>

尝试了上述脚本的每个版本(#v1 启用、#v2 启用、#v3 启用);转储进程的内存(请参阅 https://serverfault.com/a/408929/374467),并且"AAAABBBBCCCCDDDD"字符串始终显示在生成的二进制文件(包含进程内存)中。当值作为命令行参数 ($argv) 发送时也进行了测试$cc。相同的结果。

到目前为止,我发现没有可靠的方法来在PHP中做到这一点。

更新

如注释中所建议的,使用字符串文本初始化变量可能会影响结果,因为文本可能保存在内存中。因此,我更改了脚本,以便它从 cli 获取参数并通过对其应用str_rot13()来初始化变量,确保变量的内容是新的。我还添加了gc_collect_cycles()来强制垃圾回收。所以测试脚本看起来像这样:

<?php
     $cc = str_rot13($argv[1]);
     #v1:  unset($cc);
     #v2:  $cc=null;
     #v3:  for( $i=0; $i < strlen($cc); $i++ ) $cc[$i] = 'x';
     gc_collect_cycles();
     sleep(120); // enough time for taking script pid and run the memory dump
     echo "done 'n";
?>

似乎前两个方法(unset/null)不会从内存中清除$cc的新值(即使打开了gc_collect_cycles),但for循环实际上改变了它! 即使是否使用gc_collect_cycles。

使用 PHP 7.2.20 (cli) 进行测试。

注意 您仍然可以在内存中找到参数 ($argv) 的初始值!

据我了解,问题不在于使用 unset() 或 PHP 的垃圾收集器,而是确保使用的内存实际上在物理级别被清除?

简单的方法 - 只需在取消设置(或任何其他值)之前将其设置为 null 查看使用 PHP 释放内存更好的方法:unset() 或 $var = null

困难而简单的方法是用垃圾分配所有php内存,以确保其被覆盖,但我不确定虚拟内存是否固定在预定义的diapason上,或者根据进程需要进行更改。