关闭 More 保存 重做 撤销 预览

   
关闭   当前为简洁模式,您可以更新模块,修改模块属性和数据,要使用完整的拖拽功能,请点击进入高级模式
OD体育 OD体育
重播

上一主題 下一主題
»
star
LV6 呢喃的歌声
帖子    163
新博币    57 提现
提现    165.165
     
    3万 46 | 显示全部楼层 |倒序浏览
           最近,博牛大大的某位攻城狮私下找我诉苦,说我们论坛又被攻击啦,我说,咋样,又被D啦还是被C啦?他说一会儿被D,一会儿被C,烦求得很,公司都准备买高仿IP啦。于是就有了下文。在楼主的精心搜索和实践下,有了下面的一篇类原创,切勿照搬,可以模仿。
           其实很多时候,各种防攻击的思路我们都明白,比如限制IP啊,过滤攻击字符串啊,识别攻击指纹啦。可是要如何去实现它呢?用守护脚本吗?用PHP在外面包 一层过滤?还是直接加防火墙吗?这些都是防御手段。不过本文将要介绍的是直接通过nginx的普通模块和配置文件的组合来达到一定的防御效果。
    1. 验证浏览器行为
       1.1. 简易版
    我们先来做个比喻。社区在搞福利,在广场上给大家派发红包。而坏人派了一批人形的机器人(没有语言模块)来冒领红包,聪明工作人员需要想出办法来防止红包被冒领。于是工作人员在发红包之前,会给领取者一张纸,上面写着“红包拿来”,如果那人能念出纸上的字,那么就是人,给红包,如果你不能念出来,那么请自觉。于是机器人便被识破,灰溜溜地回来了。是的,在这个比喻中,人就是浏览器,机器人就是攻击器,我们可以通过鉴别cookie功能(念纸上的字)的方式来鉴别他们。下面就是nginx的配置文件写法:
    1. if ($cookie_say != "helloboniu"){
    2.     add_header Set-Cookie "say=helloboniu";
    3.     rewrite .* "$scheme://$host$uri" redirect;
    4. }
    复制代码
          让我们看下这几行的意思,当cookie中say为空时,给一个设置cookie say为helloboniu的302重定向包,如果访问者能够在第二个包中携带上cookie值,那么就能正常访问网站了,如果不能的话,那他永远活在了302中。你也可以测试一下,用CC攻击器或者webbench或者直接curl发包做测试,他们都活在了302世界中。
          当然,这么简单就能防住了?当然没有那么简单。
       1.2. 增强版
       仔细的你一定会发现配置文件这样写还是有缺陷。如果攻击者设置cookie为say=helloboniu(CC攻击器上就可以这么设置),那么这个防御就形同虚设了。我们继续拿刚刚那个比喻来说明问题。坏人发现这个规律后,给每个机器人安上了扬声器,一直重复着“红包拿来,红包拿来”,浩浩荡荡地又来领红包了。这时,工作人员的对策是这样做的,要求领取者出示有自己名字的户口本,并且念出自己的名字,“我是xxx,红包拿来”。于是一群只会嗡嗡叫着“红包拿来”的机器人又被撵回去了。当然,为了配合说明问题,每个机器人是有户口本的,被赶回去的原因是不会念自己的名字,虽然这个有点荒诞,唉。

    然后,我们来看下这种方式的配置文件写法:

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

    这样的写法和前面的区别是,不同IP的请求cookie值是不一样的,比如IP是1.2.3.4,那么需要设置的cookie是say=helloboniu1.2.3.4。于是攻击者便无法通过设置一样的cookie(比如CC攻击器)来绕过这种限制。你可以继续用CC攻击器来测试下,你会发现CC攻击器打出的流量已经全部进入302世界中。

    不过大家也能感觉到,这似乎也不是一个万全之计,因为攻击者如果研究了网站的机制之后,总有办法测出并预先伪造cookie值的设置方法。因为我们做差异化的数据源正是他们本身的一些信息(IP、user agent等)。攻击者花点时间也是可以做出专门针对网站的攻击脚本的。

    1.3. 完美版

       那么要如何根据他们自身的信息得出他们又得出他们算不出的数值?我想,聪明的你一定已经猜到了,用salt加散列。比如md5("opencdn$remote_addr"),虽然攻击者知道可以自己IP,但是他无法得知如何用他的IP来计算出这个散列,因为他是逆不出这个散列的。当然,如果你不放心的话,怕cmd5.com万一能查出来的话,可以加一些特殊字符,然后多散几次。

    很可惜,nginx默认是无法进行字符串散列的,由于楼主最近在学习lua,偶然发现了nginx的一个模块,于是我们借助nginx_lua模块来进行实现。详情请看 https://github.com/loveshell/ngx_lua_waf

    1. rewrite_by_lua '
    2.      local say = ngx.md5("helloboniu" .. 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看来,确实攻击流量和普通浏览流量是一样的。那么如何防御呢?下节会告诉你答案。

    1.2. 请求频率限制
    不得不说,很多防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("helloboniu" .. 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("helloboniu" .. 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("helloboniu" .. 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能够扛得住这些流量,然后带宽不被堵死。如果你家门被堵了,你还想开门营业,那真心没有办法了。

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

    1.3. 防扫描
    ngx_lua_waf模块

    这个是一个不错的waf模块,这块我们也就不再重复造轮子了。可以直接用这个模块来做防护,当然也完全可以再配合limit模块,用上文的思路来做到一个封IP或者封session的效果。大家可以去看看,前文中有提到链接哟。

    我能做的就只有这些了,剩下的就靠博牛的大神运维啦。最后,其实我不希望大家照搬上面的配置,需要自己去研究,比如配置参数,还有nginx各个模块的安装等,都需要自己亲自实践的。

    最后写了这么多,望博牛大神们打赏一点博币,也不枉我写了这么多。。。

    本帖由 star 于 2016-8-18 15:25 编辑
    • 12

      评分
    • +803

    Carl9896 + 5 字字珠玑
    Demo + 2 棒棒哒
    天天好心情 + 5 写得很详细
    大弩哥 + 20 感谢大大的无私分享
    fairy-123 + 5 感谢大大的无私分享
    Lily01 + 5 感谢大大的无私分享
    mdpo + 1 lz没弄明白session和cookie的机制
    robin123 + 30 感谢大大的无私分享,只能加这么多.
    程序猿Hy + 30 大神。。请收下我的膝盖。。
    Nicole + 50 不明觉厉
    daisy + 200 大神好厉害 膜拜
    小苹果 + 450 精品文章

    查看全部评分 我要评分

    个人签名

    坚持每日灌水,挣博币,早日与博牛CEO共进晚餐。。。

    TAGS DDOS, Nginx, waf, lua, 攻击
    小苹果
    LV9 苍瀚的风云
    帖子    323
    新博币    12 提现
    提现    0
    TA的勋章:勋章中心
       
      总结一下,还是需要加个门槛阻挡机器人…… 感谢LZ分享了,不仅仅是博牛用得上,现在大多数业者也整天面临攻击勒索。也用的上,干货好物,精华奉上
      个人签名

      隐忍的奋斗

      BlackHatSeo
      LV6 呢喃的歌声
      帖子    118
      新博币    240 提现
      提现    0
      TA的勋章:勋章中心
         
        楼主只讲了在WEB层面上的防御
        有些环节天生有缺陷  防御要花成本的
        上CDN 大流量来了 挨个打死
        有防御   拿个上层G口带宽照样堵死你
        :lol
        个人签名

        签名被屏蔽

        star
        LV6 呢喃的歌声
        帖子    163
        新博币    57 提现
        提现    165.165
           
          菲SEO黑白帽培训 发表于 2016-8-18 16:03
          楼主只讲了在WEB层面上的防御
          有些环节天生有缺陷  防御要花成本的
          上CDN 大流量来了 挨个打死

          这个只能阻挡那些小打小闹的,真正的对决是需要经济实力的,大量购置高配服务器,加大网络带宽,配置高防ip等。
          个人签名

          坚持每日灌水,挣博币,早日与博牛CEO共进晚餐。。。

          star
          LV6 呢喃的歌声
          帖子    163
          新博币    57 提现
          提现    165.165
             
            小苹果 发表于 2016-8-18 15:53
            总结一下,还是需要加个门槛阻挡机器人…… 感谢LZ分享了,不仅仅是博牛用得上,现在大多数业者也整天面临 ...

            多谢打赏。
            个人签名

            坚持每日灌水,挣博币,早日与博牛CEO共进晚餐。。。

            Vspint
            LV4 路旁的落叶
            帖子    12
            新博币    17 提现
            提现    0
               
              总结的很好,谢谢分享,收藏了
              个人签名

              332133238
              LV11 蕴含的太阳
              帖子    920
              新博币    0 提现
              提现    0
              TA的勋章:勋章中心
                 
                真心佩服楼主 我看了好久也不明白
                个人签名

                我不是你的主角,请不要伤害我

                长滩岛的贝壳00
                LV11 蕴含的太阳
                帖子    1600
                新博币    65 提现
                提现    0
                TA的勋章:勋章中心
                   
                  听不懂,哈哈
                  个人签名

                  小苹果
                  LV9 苍瀚的风云
                  帖子    323
                  新博币    12 提现
                  提现    0
                  TA的勋章:勋章中心
                     

                    可以学习下,对你有帮助
                    个人签名

                    隐忍的奋斗

                    dami
                    LV5 水面的小草
                    帖子    29
                    新博币    27 提现
                    提现    0
                       
                      是竞争对手么
                      个人签名

                      hi

                      点击按钮快速添加回复内容: 支持 高兴 激动 给力 加油 淡定 生气 回帖 路过 感动 感恩
                      您需要登录后才可以回帖 登录 | 立即注册

                      本版积分规则

                      关闭

                      博牛推荐上一条 /2 下一条

                      快速回复 返回顶部 返回列表