Resin上的Hash Collision DoS

 Hash Collision DoS

1、描述

Hash Collision Dos攻击的原理很简单目前很多语言使用hash来存储k-v数据包括常用的来自用户的POST数据攻击者可以通过构造请求参数, POST大量的特殊的”k”(根据每个语言的Hash算法不同而定制), 使得语言底层保存POST数据的Hash表因为冲突”(碰撞)而退化成链表,使每次读取、插入操作都需要遍历链表,造成CPU使用率100% (来源:http://coolshell.cn/articles/6424.html

Resin上的Hash Collision DoS_第1张图片

这样一来如果每次请求的数据量足够大, 那么就可以使得语言在计算查找插入的时候造成大量的CPU占用从而实现拒绝服务攻击.

例如:

在resin4.0.7服务器上:

Java HttpServletRequestImpl.getParameter() 是根据String类型的key来获取用户请求参数的值,所有的请求参数是放在java.util.HashMap中,HashMap是使用hash方法来决定key的分布,如果key.hashcode()一样,那么就会被放到同一个链表上,如下图:

Resin上的Hash Collision DoS_第2张图片


而且因为entry数量不断增多,HashMap一直申请新的Entry[]数组--hashcode的链表头,也会因为大量的ref占用jvm heap.

HashMapkey分布方法:

static int hash(int h) {
        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }



1. String.hashcode()的算法是s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1],就是每个字符的ascii值以质数31作为进制计算后得到的结果就是,31进制; 
2. 同时,有个成员变量hash保存了计算过的hashcode(), 第一次调用String.hashcode()后,计算出来的hashcode()就保存在hash成员变量中,以后就直接返回了---String是常量,String的hashcode()也永远不会变化,可以缓存在成员变量里面 3. String.hashcode()的算法已经明确了,那么就可以尝试构造k冲突。

测试环境:

cpu: 16processor

测试代码:HashCollisionMapAttack.java(注意配置heapsize)

总共16次恶意提交http://www.mytestserver.com:20080/addData.do,导致:

Resin上的Hash Collision DoS_第3张图片


同时访问

http://www.mytestserver.com:20080/  变慢。

2、解决方案

1、通过nginx/apache 限制http请求body的大小和参数的数量等,这个是现在用的最多的临时处理方案。如果服务器没有对请求的参数做限制,会受到这种攻击,如果服务器配置高,而且服务器节点多,应该在load average报警之后,继续工作一段时间。

2、升级到resin4.0.25 / resin3.1.x resin说明:http://forum.caucho.com/showthread.php?p=34786

3、升级到resin 4.0.26

resin服务使用4.0.26之后,客户端采用16线程访问服务,服务器java cpu16 processor)占用15++ %CPU时间,大概5s种之后回复正常,cpu load average 最高值在6.+,大概1分钟之后为0.+DoS客户端的请求大概在30s-60s之后得到回复,之前在4.0.7上的大量请求都没有得到回复。

Resin上的Hash Collision DoS_第4张图片

几秒中之后(最高值)

Resin上的Hash Collision DoS_第5张图片


通过测试观察resin4.0.26有较强的hash collision dos抵御能力。

但是:resin4.0.26的配置和resin4.0.7的配置文件差别较大,需要重新处理配置文件。

原理:

com.caucho.util.HashMapImpl 代替了以前使用的java.util.HashMap。重写了key的二次hash算法,每次put有可能重新分布key-vale对应的数组。数据结构和HashMap的 hashcode-list模式不同,采用的是hashcode-value模式,如果出现hashcode collision,当前hashcode会再次计算,避免o(n)次的查询操作。

推测:hashcode collision导致的再次计算和内部数组的扩容 导致%cpu暂时升高。

代码:这个Map实现类在普通情况下的查询效率不如java.util.HashMap

private V putImpl(K key, V value)
  {
    Object item = null;
    int hash = key.hashCode() & this._mask;
    int count = this._values.length;
    for (; count > 0; count--) {
      item = this._values[hash];
      if (item == null) {
        this._keys[hash] = key;
        this._values[hash] = value;
        this._size += 1;
        return null;
      }
      if (this._keys[hash].equals(key)) {
        this._values[hash] = value;
        return item;
      }
      hash = hash + 1 & this._mask;
    }
    throw new IllegalStateException();
  }

 

通过在resin.xml配置 form-parameter-max 参数来避免Hash Collision Dos

<web-app id="/" root-directory="webapps/microblog" redeploy-mode="manual">

        <form-parameter-max>${form_parameter_max?:100}</form-parameter-max>   

 </web-app>

 


4、参考文章

http://coolshell.cn/articles/6424.html
http://www.laruence.com/2011/12/29/2412.html

5、下载代码

HashCollisionMapAttack.java
http://download.csdn.net/detail/liu251/4153536



你可能感兴趣的:(算法,HashMap,服务器,dos,null,语言)