惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Stack Overflow Blog
Stack Overflow Blog
云风的 BLOG
云风的 BLOG
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Recent Announcements
Recent Announcements
Microsoft Security Blog
Microsoft Security Blog
Microsoft Azure Blog
Microsoft Azure Blog
J
Java Code Geeks
D
DataBreaches.Net
U
Unit 42
P
Proofpoint News Feed
I
InfoQ
Apple Machine Learning Research
Apple Machine Learning Research
Google DeepMind News
Google DeepMind News
博客园 - Franky
博客园_首页
IT之家
IT之家
博客园 - 叶小钗
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 【当耐特】
Hugging Face - Blog
Hugging Face - Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
阮一峰的网络日志
阮一峰的网络日志

博客园 - MK2

cnpmcore 超大 JSON parse 性能优化 基于 Postgres 实现一个 Job Queue Graceful exit with cluster and pm Use Blanket.js instead of jscover 使用 connect-domain 捕获异步调用中出现的异常 fibonacci(40) benchmark [nodejs]保证你的程序死了还能复活:forever and forever webui [nodejs]Buffer vs String Nodejs "Hello world" benchmark npm 资源库镜像 NodeBlog v0.2.0 更新说明 关于__proto__的链式记忆 NAE支持自定义域名了 Nodejs 离线文档下载 nodejs读写大文件 Nodejs HTTP请求的超时处理(Nodejs HTTP Client Request Timeout Handle) 在jQuery 1.5+ 的jqXHR上监听文件上传进度(xhr.upload) 关于Nodejs中Buffer释放的二三事 nodejs web开发入门: Simple-TODO Nodejs 实现版
Defense hash algorithm collision 防御hash算法冲突导致拒绝...
MK2 · 2012-01-01 · via 博客园 - MK2

问题

一篇#2011-003 multiple implementations denial-of-service via hash algorithm collision 文章,引起了 Web 应用安全领域的骚动。

这种hash算法冲突的原理到底是什么?可以看看一下两篇以PHP为例子的说明文章:

解决办法

既然是语言层面的hash算法冲突导致的,那么这种冲突就无法避免了。
但是我们可以限制Hash key的数量来避免。

所以可以进行一下设置解决此问题:

  • 限制请求大小 (只能指标不治本,因为PHP版本的话,700KB的数据就足以发起攻击)
  • 限制请求key的数量 (这可以基本解决问题,除非应用无法预知最大的key数量)
  • 只接受允许的key (这是终极方法,但需要保证开发记得配置新增的key)

在Nodejs防御此问题

虽然在目前为止还没看到对Nodejs造成攻击的具体方法,但是还是以防范于未然为原则,需要对此问题做好充分的防御措施。

Nodejs 的攻击方法已经出现,具体测试结果可以查看 Hash algorithm collision in Nodejs
hac-results

由于我个人一直使用的是 connect ,所以我以 connect 为示例说明吧 ^_^

使用 connect.limit 限制 request-body-size

Issue #446 已有同学提出此问题了,@TJ回复如下:

we have limit() for this, which works for any request body. Even without this specific issue you could exhaust resources reasonably easily without some form of limiting

好吧,直接上 connect.limit 模块解决

connect()
  .use(connect.limit('1mb'))
  .use(handleRequest)

修改 qs 模块,让其支持 keys-limit 和 allow-keys

querystring.js

PS: 提了pull request,但是估计在没有真实攻击示例放出来之前,是不会被接受的。

/**
 * Parse the given str.
 */

function parseString(str, options) {
  var limit = options && options.limit;
  var keys = options && options.keys;
  if (keys && Array.isArray(keys)) {
    keys = {};
    for (var i = 0, l = options.keys.length; i < l; i++) {
      keys[options.keys[i]] = 1;
    }
  }
  return String(str)
    .split('&', limit)
    .reduce(function(ret, pair){
      try{
        pair = decodeURIComponent(pair.replace(/\+/g, ' '));
      } catch(e) {
        // ignore
      }

      var eql = pair.indexOf('=')
        , brace = lastBraceInKey(pair)
        , key = pair.substr(0, brace || eql);
      if (keys && !keys[key]) {
        return ret;
      }
      var val = pair.substr(brace || eql, pair.length)
      val = val.substr(val.indexOf('=') + 1, val.length);

      // ?foo
      if ('' == key) key = pair, val = '';

      return merge(ret, key, val);
    }, { base: {} }).base;
}


/**
 * Parse the given query `str` or `obj`, returning an object.
 *
 * Options: (only effect on parse string)
 *
 *  - `limit` parse string split limit.
 *  - `keys`  which keys need to be parse.
 *
 * @param {String} str | {Object} obj
 * @param {Object} options
 * @return {Object}
 * @api public
 */

exports.parse = function(str, options) {
  if (null == str || '' == str) return {};
  return 'object' == typeof str
    ? parseObject(str)
    : parseString(str, options);
};

还需要让 connect.query 模块 传递options参数给 qs.parse()

module.exports = function query(options){
  return function query(req, res, next){
    req.query = ~req.url.indexOf('?')
      ? qs.parse(parse(req.url).query, options)
      : {};
    next();
  };
};

同样 connect.urlencoded 模块也需要将options参数传递给 qs.parse()

    req.on('end', function(){
      try {
        req.body = buf.length
          ? qs.parse(buf, options)
          : {};
        next();
      } catch (err){
        next(err);
      }
    });

全部组合起来

var qsOptions = { limit: 100 };
connect()
  .use(connect.limit('1mb'))
  .use(connect.query(qsOptions))
  .use(connect.bodyParser(qsOptions))
  .use(handleRequest)

防范 http header 攻击

请求的 http header 也会导致hash冲突,在V8层面未修复hash算法之前,可以通过简单的 http_patch.js 修复此问题:

var http = require('http');

var IncomingMessage = http.IncomingMessage;
var _addHeaderLine = IncomingMessage.prototype._addHeaderLine;

// limit http header number
IncomingMessage.prototype._addHeaderLine = function(field, val) {
  if (!this.__headerCount__) {
    this.__headerCount__ = 0;
  } else if (this.__headerCount__ >= 100) {
    return;
  }
  _addHeaderLine.apply(this, arguments);
  this.__headerCount__++;
};

最后

2011年末,苦逼的程序员还在为这个安全漏洞写补丁,打补丁,想各种解决方法。
2012年或许还会有各种各样的问题,我们一起勇敢面对吧。
Happy New Year!