聘我网

新概念招聘3.0

关于PHP数组的hash攻击案例

vote up0vote downstar

先上鸟哥提供的一段代码:

<?php
$size = pow(2, 16);
$startTime = microtime(true);
$array = array();
for($key = 0, $maxKey = ($size - 1) * $size; $key <= $maxKey; $key += $size) 
{
    $array[$key] = 0;
}
$endTime = microtime(true);
echo '插入 ', $size, ' 个恶意的元素需要 ', $endTime - $startTime, ' 秒', "\n";

$startTime = microtime(true);
$array = array();
for ($key = 0, $maxKey = $size - 1; $key <= $maxKey; ++$key) {
     $array[$key] = 0;
}
$endTime = microtime(true);
echo '插入 ', $size, ' 个普通元素需要 ', $endTime - $startTime, ' 秒', "\n";

在我的机子上跑下来是:

插入 65536 个恶意的元素需要 193.47305703163 秒
插入 65536 个普通元素需要 0.088220834732056 秒

那么恶意元素构造原理到底是什么?

先要知道PHP的数组的一些内幕:

在PHP中,如果键值是数字, 那么Hash的时候就是数字本身, 一般的时候都是, index & tableMask. 而tableMask是用来保证数字索引不会超出数组可容纳的元素个数值, 也就是数组个数-1.

PHP的Hashtable的大小都是2的指数, 比如如果你存入10个元素的数组, 那么数组实际大小是16, 如果存入20个, 则实际大小为32, 而63个话, 实际大小为64. 当你的存入的元素个数大于了数组目前的最多元素个数的时候, PHP会对这个数组进行扩容, 并且从新Hash.

然后回过去看恶意元素的构造

每个键都是2^16的倍数

从而必然是一路扩容而来的Hashtable的大小的倍数

即键值对Hashtable的大小取模一直是0

从而每次都会分配到同一个桶,

Hashtable蜕化成了链表

这样在每次插入的时候PHP都需要遍历一遍这个链表, 大家可以想象, 第一次插入, 需要遍历0个元素, 第二次是1个, 第三次是3个, 第65536个是65535个, 那么总共就需要65534*65535/2=2147385345次遍历….

以上90%的文字都是转载

用自己习惯的逻辑重整了下

参考:

PHP数组的Hash冲突实例

 

您的回答





不是您要找的问题? 浏览其他含有标签 的问题或者 自己问个.