我必须在项目中使用用户提供的数据进行反序列化。 IFAIK使用unserialize与不受信任的数据是不安全的,因为可能通过隐式调用wakeup-function和析构函数(https://wiki.php.net/rfc/secure_unserialize)来执行用户代码。
好的,我试过这样的事情
$a = serialize(array('a'=>'b', 'c'=>20));
unserialize($a, array('allowed_classes'=>false));
但是反序列化会以这种方式忽略所有数据......
所以,问题是: 我如何反序列化字符串包含数组和基本数据类型(如整数,字符串,bool等),但保护自己不会反序列化不需要的类的对象(并执行不需要的代码)?
提前致谢。
答案 0 :(得分:2)
您没有提到您使用的是哪个版本的PHP,但unserialize
的第二个参数仅在PHP 7中可用。
来自php.net:
7.0.0已添加选项参数。
PHP 5.5.9-1ubuntu4.20(cli):
php > $a = serialize(array('a'=>'b', 'c'=>20));
php > var_dump(unserialize($a, array('allowed_classes'=>false)));
PHP Warning: unserialize() expects exactly 1 parameter, 2 given in php shell code on line 1
PHP Stack trace:
PHP 1. {main}() php shell code:0
PHP 2. unserialize() php shell code:1
bool(false)
PHP 7.0.12(cli):
php > $a = serialize(array('a'=>'b', 'c'=>20));
php > var_dump(unserialize($a, array('allowed_classes'=>false)));
array(2) {
["a"]=>
string(1) "b"
["c"]=>
int(20)
}
带有序列化对象的PHP 7.0.12(cli):
php > $a = serialize(array('a'=>'b', 'c'=>20, 'd'=> new Exception));
php > var_dump(unserialize($a, array('allowed_classes'=>false)));
array(3) {
["a"]=>
string(1) "b"
["c"]=>
int(20)
["d"]=>
object(__PHP_Incomplete_Class)#1 (8) {
["__PHP_Incomplete_Class_Name"]=>
string(9) "Exception"
["message":protected]=>
string(0) ""
["string":"Exception":private]=>
string(0) ""
["code":protected]=>
int(0)
["file":protected]=>
string(14) "php shell code"
["line":protected]=>
int(1)
["trace":"Exception":private]=>
array(0) {
}
["previous":"Exception":private]=>
NULL
}
}
我无法说明它的安全性或完整性,但unserialize
php文档中的comment 119851使用>的options参数为unserialize
赋予了这个实现。 PHP 5.3:
function php7_unserialize($str, $options = array())
{
if(version_compare(PHP_VERSION, '7.0.0', '>='))
{ return unserialize($str, $options); }
$allowed_classes = isset($options['allowed_classes']) ?
$options['allowed_classes'] : true;
if(is_array($allowed_classes) || !$allowed_classes)
{
$str = preg_replace_callback(
'/(?=^|:)(O|C):\d+:"([^"]*)":(\d+):{/',
function($matches) use ($allowed_classes)
{
if(is_array($allowed_classes) &&
in_array($matches[2], $allowed_classes))
{ return $matches[0]; }
else
{
return $matches[1].':22:"__PHP_Incomplete_Class":'.
($matches[3] + 1).
':{s:27:"__PHP_Incomplete_Class_Name";'.
serialize($matches[2]);
}
},
$str
);
}
unset($allowed_classes);
return unserialize($str);
}