一般的问题是:在 PHP 中未设置该变量后,是否可以从物理内存中删除字符串变量的值?
该问题源于安全标准的某些要求(在处理某些重要数据时,不应将数据从内存转储到磁盘)。根据“内存加密吗? ”主题没有加密内存数据的好方法。
因此,在 PHP 中取消设置 String 变量时,您不能确定内存中的数据已被覆盖。关于将新值设置为变量的相同故事。
因此,我很感兴趣是否可以在不更改unset
方法核心代码的情况下从内存中清除变量值?
一般的问题是:在 PHP 中未设置该变量后,是否可以从物理内存中删除字符串变量的值?
该问题源于安全标准的某些要求(在处理某些重要数据时,不应将数据从内存转储到磁盘)。根据“内存加密吗? ”主题没有加密内存数据的好方法。
因此,在 PHP 中取消设置 String 变量时,您不能确定内存中的数据已被覆盖。关于将新值设置为变量的相同故事。
因此,我很感兴趣是否可以在不更改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”字符串始终显示在生成的二进制文件中(其中包含进程内存)。当 $cc 值作为命令行参数 ($argv) 发送时也进行了测试。结果相同。
迄今为止,我发现在 PHP 中没有可靠的方法来做到这一点。
更新
正如评论中所建议的,使用字符串文字来初始化变量可能会影响结果,因为文字可能保存在内存中。因此,我更改了脚本,使其从 cli 获取参数并通过应用str_rot13()
来初始化变量,确保变量的内容是new。我还添加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 内存,以确保其被覆盖,但我不确定虚拟内存是否固定在预定义的音调上,或者根据进程需要进行更改。