经过nginx配置文件抵御攻击,防护CC攻击的经典思路!

你们好,咱们是OpenCDN团队的Twwy。此次咱们来说讲如何经过简单的配置文件来实现nginx防护攻击的效果。html

其实不少时候,各类防攻击的思路咱们都明白,好比限制IP啊,过滤攻击字符串啊,识别攻击指纹啦。但是要如何去实现它呢?用守护脚本吗?用PHP在外面包 一层过滤?仍是直接加防火墙吗?这些都是防护手段。不过本文将要介绍的是直接经过nginx的普通模块和配置文件的组合来达到必定的防护效果。nginx

0x01 验证浏览器行为web

简易版浏览器

咱们先来作个比喻。服务器

社区在搞福利,在广场上给你们派发红包。而坏人派了一批人形的机器人(没有语言模块)来冒领红包,聪明工做人员须要想出办法来防止红包被冒领。cookie

因而工做人员在发红包以前,会给领取者一张纸,上面写着“红包拿来”,若是那人能念出纸上的字,那么就是人,给红包,若是你不能念出来,那么请自觉。因而机器人便被识破,灰溜溜地回来了。session

是的,在这个比喻中,人就是浏览器,机器人就是攻击器,咱们能够经过鉴别cookie功能(念纸上的字)的方式来鉴别他们。下面就是nginx的配置文件写法。框架

  1. if ($cookie_say != "hbnl"){
  2.      add_header Set-Cookie "say=hbnl";
  3.      rewrite .* "$scheme://$host$uri" redirect;
  4. }

让咱们看下这几行的意思,当cookie中say为空时,给一个设置cookie say为hbnl的302重定向包,若是访问者可以在第二个包中携带上cookie值,那么就能正常访问网站了,若是不能的话,那他永远活在了302中。你也能够测试一下,用CC攻击器或者webbench或者直接curl发包作测试,他们都活在了302世界中。dom

固然,这么简单就能防住了?固然没有那么简单。curl

加强版

仔细的你必定会发现配置文件这样写仍是有缺陷。若是攻击者设置cookie为say=hbnl(CC攻击器上就能够这么设置),那么这个防护就形同虚设了。咱们继续拿刚刚那个比喻来讲明问题。

坏人发现这个规律后,给每一个机器人安上了扬声器,一直重复着“红包拿来,红包拿来”,浩浩荡荡地又来领红包了。

这时,工做人员的对策是这样作的,要求领取者出示有本身名字的户口本,而且念出本身的名字,“我是xxx,红包拿来”。因而一群只会嗡嗡叫着“红包拿来”的机器人又被撵回去了。

固然,为了配合说明问题,每一个机器人是有户口本的,被赶回去的缘由是不会念本身的名字,虽然这个有点荒诞,唉。

而后,咱们来看下这种方式的配置文件写法

  1. if ($cookie_say != "hbnl$remote_addr"){
  2.      add_header Set-Cookie "say=hbnl$remote_addr";
  3.      rewrite .* "$scheme://$host$uri" redirect;
  4. }

这样的写法和前面的区别是,不一样IP的请求cookie值是不同的,好比IP是1.2.3.4,那么须要设置的cookie是say=hbnl1.2.3.4。因而攻击者便没法经过设置同样的cookie(好比CC攻击器)来绕过这种限制。你能够继续用CC攻击器来测试下,你会发现CC攻击器打出的流量已经所有进入302世界中。

不过你们也能感受到,这彷佛也不是一个万全之计,由于攻击者若是研究了网站的机制以后,总有办法测出并预先伪造cookie值的设置方法。由于咱们作差别化的数据源正是他们自己的一些信息(IP、user agent等)。攻击者花点时间也是能够作出专门针对网站的攻击脚本的。

完美版

那么要如何根据他们自身的信息得出他们又得出他们算不出的数值?

我想,聪明的你必定已经猜到了,用salt加散列。好比md5("opencdn$remote_addr"),虽然攻击者知道能够本身IP,可是他没法得知如何用他的IP来计算出这个散列,由于他是逆不出这个散列的。固然,若是你不放心的话,怕cmd5.com万一能查出来的话,能够加一些特殊字符,而后多散几回。

很惋惜,nginx默认是没法进行字符串散列的,因而咱们借助nginx_lua模块来进行实现。

  1. rewrite_by_lua '
  2.      local say = ngx.md5("opencdn" .. ngx.var.remote_addr)
  3.      if (ngx.var.cookie_say ~= say) then
  4.          ngx.header["Set-Cookie"] = "say=" .. say
  5.          return ngx.redirect(ngx.var.scheme .. "://" .. ngx.var.host .. ngx.var.uri)
  6.      end
  7. ';

经过这样的配置,攻击者便没法事先计算这个cookie中的say值,因而攻击流量(代理型CC和低级发包型CC)便在302地狱没法自拔了。

你们能够看到,除了借用了md5这个函数外,其余的逻辑和上面的写法是如出一辙的。所以若是能够的话,你彻底能够安装一个nginx的计算散列的第三方模块来完成,可能效率会更高一些。

这段配置是能够被放在任意的location里面,若是你的网站有对外提供API功能的话,建议API必定不能加入这段,由于API的调用也是没有浏览器行为的,会被当作攻击流量处理。而且,有些弱一点爬虫也会陷在302之中,这个须要注意。

同时,若是你以为set-cookie这个动做彷佛攻击者也有可能经过解析字符串模拟出来的话,你能够把上述的经过header来设置cookie的操做,变成经过高端大气的js完成,发回一个含有doument.cookie=...的文本便可。

那么,攻击是否是彻底被挡住了呢?只能说那些低级的攻击已经被挡住而来,若是攻击者必须花很大代价给每一个攻击器加上webkit模块来解析js和执行set-cookie才行,那么他也是能够逃脱302地狱的,在nginx看来,确实攻击流量和普通浏览流量是同样的。那么如何防护呢?下节会告诉你答案。

0x02 请求频率限制

不得不说,不少防CC的措施是直接在请求频率上作限制来实现的,可是,不少都存在着必定的问题。

那么是哪些问题呢?

首先,若是经过IP来限制请求频率,容易致使一些误杀,好比我一个地方出口IP就那么几个,而访问者一多的话,请求频率很容易到上限,那么那个地方的用户就都访问不了你的网站了。

因而你会说,我用SESSION来限制就有这个问题了。嗯,你的SESSION为攻击者敞开了一道大门。为何呢?看了上文的你可能已经大体知道了,由于就像那个“红包拿来”的扬声器同样,不少语言或者框架中的SESSION是可以伪造的。以PHP为例,你能够在浏览器中的cookie看到PHPSESSIONID,这个ID不一样的话,session也就不一样了,而后若是你杜撰一个PHPSESSIONID过去的话,你会发现,服务器也承认了这个ID,为这个ID初始化了一个会话。那么,攻击者只须要每次发完包就构造一个新的SESSIONID就能够很轻松地躲过这种在session上的请求次数限制。

那么咱们要如何来作这个请求频率的限制呢?

首先,咱们先要一个攻击者没法杜撰的sessionID,一种方式是用个池子记录下每次给出的ID,而后在请求来的时候进行查询,若是没有的话,就拒绝请求。这种方式咱们不推荐,首先一个网站已经有了session池,这样再作个无疑有些浪费,并且还须要进行池中的遍历比较查询,太消耗性能。咱们但愿的是一种能够无状态性的sessionID,能够吗?能够的。

  1. rewrite_by_lua '
  2.      local random = ngx.var.cookie_random
  3.      if(random == nil) then
  4.          random = math.random(999999)
  5.      end
  6.      local token = ngx.md5("opencdn" .. ngx.var.remote_addr .. random)
  7.      if (ngx.var.cookie_token ~= token) then
  8.          ngx.header["Set-Cookie"] = {"token=" .. token, "random=" .. random}
  9.          return ngx.redirect(ngx.var.scheme .. "://" .. ngx.var.host .. ngx.var.uri)
  10.      end
  11. ';

你们是否是以为好像有些眼熟?是的,这个就是上节的完美版的配置再加个随机数,为的是让同一个IP的用户也能有不一样的token。一样的,只要有nginx的第三方模块提供散列和随机数功能,这个配置也能够不用lua直接用纯配置文件完成。

有了这个token以后,至关于每一个访客有一个没法伪造的而且独一无二的token,这种状况下,进行请求限制才有意义。

因为有了token作铺垫,咱们能够不作什么白名单、黑名单,直接经过limit模块来完成。

  1. http{
  2.      ...
  3.      limit_req_zone $cookie_token zone=session_limit:3m rate=1r/s;
  4. }

而后咱们只须要在上面的token配置后面中加入

  1. limit_req zone=session_limit burst=5;

因而,又是两行配置便让nginx在session层解决了请求频率的限制。不过彷佛仍是有缺陷,由于攻击者能够经过一直获取token来突破请求频率限制,若是能限制一个IP获取token的频率就更完美了。能够作到吗?能够。

  1. http{
  2.      ...
  3.      limit_req_zone $cookie_token zone=session_limit:3m rate=1r/s;
  4.      limit_req_zone $binary_remote_addr $uri zone=auth_limit:3m rate=1r/m;
  5. }
  1. location /{
  2.      limit_req zone=session_limit burst=5;
  3.      rewrite_by_lua '
  4.      local random = ngx.var.cookie_random
  5.      if (random == nil) then
  6.          return ngx.redirect("/auth?url=" .. ngx.var.request_uri)
  7.      end
  8.      local token = ngx.md5("opencdn" .. ngx.var.remote_addr .. random)
  9.      if (ngx.var.cookie_token ~= token) then
  10.          return ngx.redirect("/auth?url=".. ngx.var.request_uri)
  11.      end
  12. ';
  13. }
  14. location /auth {
  15.      limit_req zone=auth_limit burst=1;
  16.      if ($arg_url = "") {
  17.          return403;
  18.      }
  19.      access_by_lua '
  20.          local random = math.random(9999)
  21.          local token = ngx.md5("opencdn" .. ngx.var.remote_addr .. random)
  22.          if (ngx.var.cookie_token ~= token) then
  23.              ngx.header["Set-Cookie"] = {"token=" .. token, "random=" .. random}
  24.              return ngx.redirect(ngx.var.arg_url)
  25.          end
  26.      ';
  27. }

我想你们也应该已经猜到,这段配置文件的原理就是:把原本的发token的功能分离到一个auth页面,而后用limit对这个auth页面进行频率限制便可。这边的频率是1个IP每分钟受权1个token。固然,这个数量能够根据业务须要进行调整。

须要注意的是,这个auth部分我lua采用的是access_by_lua,缘由在于limit模块是在rewrite阶段后执行的,若是在rewrite阶段302的话,limit将会失效。所以,这段lua配置我不能保证能够用原生的配置文件实现,由于不知道如何用配置文件在rewrite阶段后进行302跳转,也求大牛可以指点一下啊。

固然,你若是还不知足于这种限制的话,想要作到某个IP若是一天到达上限超过几回以后就直接封IP的话,也是能够的,你能够用相似的思路再作个错误页面,而后到达上限以后不返回503而是跳转到那个错误页面,而后错误页面也作个请求次数限制,好比天天只能访问100次,那么当超过报错超过100次(请求错误页面100次)以后,那天这个IP就不能再访问这个网站了。

因而,经过这些配置咱们便实现了一个网站访问频率限制。不过,这样的配置也不是说能够彻底防止了攻击,只能说让攻击者的成本变高,让网站的扛攻击能力变强,固然,前提是nginx可以扛得住这些流量,而后带宽不被堵死。若是你家门被堵了,你还想开门营业,那真心没有办法了。

而后,作完流量上的防御,让咱们来看看对于扫描器之类的攻击的防护。

0x03 防扫描

ngx_lua_waf模块

这个是一个不错的waf模块,这块咱们也就再也不重复造轮子了。能够直接用这个模块来作防御,固然也彻底能够再配合limit模块,用上文的思路来作到一个封IP或者封session的效果。

0x04 总结

本文旨在达到抛砖引玉的做用,咱们并不但愿你直接单纯的复制咱们的这些例子中的配置,而是但愿根据你的自身业务须要,写出适合自身站点的配置文件。

源文地址

本文地址:http://www.92csz.com/30/1255.html
如非注明则为本站原创文章,欢迎转载。转载请注明转载自:moon's blog

相关文章
相关标签/搜索