相关推荐recommended
Nginx攻击防护、CC防护、防止SQL注入、防XSS的实践配置方法
作者:mmseoamin日期:2024-02-05

Nginx攻击防护、CC防护、防止SQL注入、防XSS的实践配置方法

    • 防止SQL注入、XSS攻击
    • Nginx安全防护屏蔽那些恶意的IP和垃圾蜘蛛
    • nginx网站攻击防护
    • Nginx简单防御CC攻击

      资源宝分享:www.httple.net

      感觉对你有用关注一下吧

      一、防止文件被下载

      比如将网站数据库导出到站点根目录进行备份,很有可能也会被别人下载,从而导致数据丢失的风险。以下规则可以防止一些常规的文件被下载,可根据实际情况增减。

      location ~ \.(zip|rar|sql|bak|gz|7z)$ {  
          return 444;     
      }
      

      二、屏蔽非常见蜘蛛(爬虫)

      如果经常分析网站日志你会发现,一些奇怪的 UA 总是频繁的来访问网站,而这些 UA 对网站收录毫无意义,反而增加服务器压力,可以直接将其屏蔽。

      if ($http_user_agent ~* (SemrushBot|python|MJ12bot|AhrefsBot|AhrefsBot|hubspot|opensiteexplorer|leiki|webmeup)) { 
          return 444;    
      }
      

      三、禁止某个目录执行脚本

      比如网站上传目录,通常存放的都是静态文件,如果因程序验证不严谨被上传木马程序,导致网站被黑。以下规则请根据自身情况改为您自己的目录,需要禁止的脚本后缀也可以自行添加。

      #uploads|templets|data 这些目录禁止执行 PHP    
      location ~* ^/(uploads|templets|data)/.*.(php|php5)$ { 
          return 444;
      }
      

      四、屏蔽某个 IP 或 IP 段

      如果网站被恶意灌水或 CC 攻击,可从网站日志中分析特征 IP,将其 IP 或 IP 段进行屏蔽。

      #屏蔽 192.168.5.23 这个 IP
      deny 192.168.5.23;
      #屏蔽 192.168.5.* 这个段
      denu 192.168.5.0/24;
      

      上面规则报道查看 444 状态码而不是 403,

      因为 444 状态码在 nginx 的中有特殊含义,nginx 的 444 状态是直接由服务器中断连接,不会向客户端再返回任何消息。

      比返回 403 更加暴力

      防止SQL注入、XSS攻击

      将下面的Nginx配置文件代码放入到对应站点的.conf配置文件[server]里,然后重启Nginx即可生效。

       if ($request_method !~* GET|POST) { return 444; }
       #使用444错误代码可以更加减轻服务器负载压力。
       #防止SQL注入
       if ($query_string ~* (\$|'|--|[+|(%20)]union[+|(%20)]|[+|(%20)]insert[+|(%20)]|[+|(%20)]drop[+|(%20)]|[+|(%20)]truncate[+|(%20)]|[+|(%20)]update[+|(%20)]|[+|(%20)]from[+|(%20)]|[+|(%20)]grant[+|(%20)]|[+|(%20)]exec[+|(%20)]|[+|(%20)]where[+|(%20)]|[+|(%20)]select[+|(%20)]|[+|(%20)]and[+|(%20)]|[+|(%20)]or[+|(%20)]|[+|(%20)]count[+|(%20)]|[+|(%20)]exec[+|(%20)]|[+|(%20)]chr[+|(%20)]|[+|(%20)]mid[+|(%20)]|[+|(%20)]like[+|(%20)]|[+|(%20)]iframe[+|(%20)]|[\<|%3c]script[\>|%3e]|javascript|alert|webscan|dbappsecurity|style|confirm\(|innerhtml|innertext)(.*)$) { return 555; }
       if ($uri ~* (/~).*) { return 501; }
       if ($uri ~* (\\x.)) { return 501; }
       #防止SQL注入 
       if ($query_string ~* "[;'<>].*") { return 509; }
       if ($request_uri ~ " ") { return 509; }
       if ($request_uri ~ (\/\.+)) { return 509; }
       if ($request_uri ~ (\.+\/)) { return 509; }
       
       #if ($uri ~* (insert|select|delete|update|count|master|truncate|declare|exec|\*|\')(.*)$ ) { return 503; }
       #防止SQL注入
       if ($request_uri ~* "(cost\()|(concat\()") { return 504; }
       if ($request_uri ~* "[+|(%20)]union[+|(%20)]") { return 504; }
       if ($request_uri ~* "[+|(%20)]and[+|(%20)]") { return 504; }
       if ($request_uri ~* "[+|(%20)]select[+|(%20)]") { return 504; }
       if ($request_uri ~* "[+|(%20)]or[+|(%20)]") { return 504; }
       if ($request_uri ~* "[+|(%20)]delete[+|(%20)]") { return 504; }
       if ($request_uri ~* "[+|(%20)]update[+|(%20)]") { return 504; }
       if ($request_uri ~* "[+|(%20)]insert[+|(%20)]") { return 504; }
       if ($query_string ~ "(<|%3C).*script.*(>|%3E)") { return 505; }
       if ($query_string ~ "GLOBALS(=|\[|\%[0-9A-Z]{0,2})") { return 505; }
       if ($query_string ~ "_REQUEST(=|\[|\%[0-9A-Z]{0,2})") { return 505; }
       if ($query_string ~ "proc/self/environ") { return 505; }
       if ($query_string ~ "mosConfig_[a-zA-Z_]{1,21}(=|\%3D)") { return 505; }
       if ($query_string ~ "base64_(en|de)code\(.*\)") { return 505; }
       if ($query_string ~ "[a-zA-Z0-9_]=http://") { return 506; }
       if ($query_string ~ "[a-zA-Z0-9_]=(\.\.//?)+") { return 506; }
       if ($query_string ~ "[a-zA-Z0-9_]=/([a-z0-9_.]//?)+") { return 506; }
       if ($query_string ~ "b(ultram|unicauca|valium|viagra|vicodin|xanax|ypxaieo)b") { return 507; }
       if ($query_string ~ "b(erections|hoodia|huronriveracres|impotence|levitra|libido)b") {return 507; }
       if ($query_string ~ "b(ambien|bluespill|cialis|cocaine|ejaculation|erectile)b") { return 507; }
       if ($query_string ~ "b(lipitor|phentermin|pro[sz]ac|sandyauer|tramadol|troyhamby)b") { return 507; }
       #这里大家根据自己情况添加删减上述判断参数,cURL、wget这类的屏蔽有点儿极端了,但要“宁可错杀一千,不可放过一个”。
       if ($http_user_agent ~* YisouSpider|ApacheBench|WebBench|Jmeter|JoeDog|Havij|GetRight|TurnitinBot|GrabNet|masscan|mail2000|github|wget|curl|Java|python) { return 508; }
       #同上,大家根据自己站点实际情况来添加删减下面的屏蔽拦截参数。
       if ($http_user_agent ~* "Go-Ahead-Got-It") { return 508; }
       if ($http_user_agent ~* "GetWeb!") { return 508; }
       if ($http_user_agent ~* "Go!Zilla") { return 508; }
       if ($http_user_agent ~* "Download Demon") { return 508; }
       if ($http_user_agent ~* "Indy Library") { return 508; }
       if ($http_user_agent ~* "libwww-perl") { return 508; }
       if ($http_user_agent ~* "Nmap Scripting Engine") { return 508; }
       if ($http_user_agent ~* "~17ce.com") { return 508; }
       if ($http_user_agent ~* "WebBench*") { return 508; }
       if ($http_user_agent ~* "spider") { return 508; } #这个会影响国内某些搜索引擎爬虫,比如:搜狗
       #拦截各恶意请求的UA,可以通过分析站点日志文件或者waf日志作为参考配置。
       if ($http_referer ~* 17ce.com) { return 509; }
       #拦截17ce.com站点测速节点的请求,所以明月一直都说这些测速网站的数据仅供参考不能当真的。
       if ($http_referer ~* WebBench*") { return 509; }
       #拦截WebBench或者类似压力测试工具,其他工具只需要更换名称即可。
      

      Nginx安全防护屏蔽那些恶意的IP和垃圾蜘蛛

      机器人自动扫描漏洞的~可真烦人!

      我这个只合适 nginx.Apache 自己修改!

      #屏蔽蜘蛛
      https://jacdn.kieng.cn/webfile/deny-bots.conf
       
      #屏蔽 IP
      https://jacdn.kieng.cn/webfile/deny-ips.conf
       
      #屏蔽 IP 精华版
      https://jacdn.kieng.cn/webfile/deny-ip-acc.conf
      

      用法:

      下载上面的文件到你的nginx目录

      在server块中添加include 目录*/deny-bots.conf*;(如果是跟 nginx 配置文件放在了一个目录那直接 include deny-bots.conf😉

      如:

      server
       
      {
       
      ......
       
      include deny-bots.conf;
      ......
       
      }
      

      完事记得重启!要不然不生效!

      service nginx reload

      再见!垃圾!

      IP 数据是根据,网络权威机构评判的!

      nginx网站攻击防护

      发现问题当然要立马解决了,当时思路就是统计nginx日志,当单个ip在10秒钟内访问 /account/sendPhoneCode次数超过5次,就禁用这个ip,正常用户不可能有么大的访问量,于是就有了下面的防攻击shell脚本。

      这个脚本加在定时任务里每分钟执行一次,半夜0点自动重启动防火墙,释放IP,基本上防止了攻击,大概使用了半个月。

      #!/bin/bash
      #write: lijing 
      #description:拦截非法IP
         
      #定义变量
      RETVAL=0
      Date=$( date '+%Y-%m-%d' )
      Time=$( date '+%Y:%H:%M' -d '-1 minute' )
      MON=$( date | awk -F " " '{print $2}' )
      TODAY=$( date | awk -F " " '{print $3}' )
      Log= "/data/logs/nginx/access.log "
      LINE= "70000"
         
      #关键字
      Key01= "sendPhoneCode"
         
      Status= /tmp/statuS_deny_ip
         
      /sbin/service iptables status > $Status
         
      #定义函数
      #禁止时间函数
      secure_deny_time(){
      Time01=$( date "+%H:%M:%S" -d " -10 second" )
      Time02=$( date "+%H:%M:%S" -d " -9  second" )
      Time03=$( date "+%H:%M:%S" -d " -8  second" )
      Time04=$( date "+%H:%M:%S" -d " -7  second" )
      Time05=$( date "+%H:%M:%S" -d " -6  second" )
      Time06=$( date "+%H:%M:%S" -d " -5  second" )
      Time07=$( date "+%H:%M:%S" -d " -4  second" )
      Time08=$( date "+%H:%M:%S" -d " -3  second" )
      Time09=$( date "+%H:%M:%S" -d " -2  second" )
      Time10=$( date "+%H:%M:%S" -d " -1  second" )
           echo  "$Time01  $Time02 $Time03 $Time04 $Time05 $Time06 $Time07 $Time08 $Time09 $Time10 "
      }
      #       禁止关键字函数
      secure_key(){
           tail -n $LINE $LOG | grep "$TODAY\/$MON" | grep - v ^$| grep $TIME| grep $1 | grep $2 | grep $3  | grep $4 | awk -F " " '{print $1}' | sort >> $Deny
           echo " grep " $TODAY\/$MON " $LOG |grep -v ^$|grep $TIME|grep $1 |grep $2 |grep $3  |grep $4 |awk '{print $1}' |sort"
               }
      #执行防火墙拦截函数
      secure_deny_ip()
      {
               cat $Deny
               echo ......................
               cat $Deny02
           for i in $IP; do
               NUM=$( cat $Deny02| grep $i| awk -F " " '{print $1}' )
              if [ -z $NUM ]; then
                   echo " "
               else
                   if [ $NUM - ge $Dot ]; then
                       for y in $i; do
                           grep $y $Status  > /dev/null 2>&1
                           RETVAL=$?
                                               [ $RETVAL != 0  ] && echo "/sbin/iptables -I INPUT -s $y  -j DROP"
      [ $RETVAL != 0  ] && /sbin/iptables -I INPUT -s $y  -j DROP
                                               [ $RETVAL != 0  ] && echo "$(date " +%H:%M:%S ") $y " >> /tmp/ $Date
                           #[ $RETVAL != 0  ] && /sbin/iptables -I INPUT -s $y -p  tcp  -j REJECT
                       done
                   fi
               fi
           done
      }
         
         
      NUMBER= "1 2 3 4 5 6"
      for  NUMBER in  $NUMBER   ; do
      sleep 10s
      #定义点击次数 Dot
      Dot=5
      Deny= /tmp/secure_deny_tmp_ $NUMBER
      Deny02= /tmp/secure_deny_ $NUMBER
      #第1次,检查当前时间以前10s.  如: 0-10秒
      echo "第$NUMBER 次,检查当前时间以前第$NUMBER 个10s.大于 $Dot 次攻击阻止"
      echo > $Deny
      for LOG in ` echo $Log` ; do
           secure_deny_time
           for TIME in $Time01  $Time02 $Time03 $Time04 $Time05 $Time06 $Time07 $Time08 $Time09 $Time10 ; do
               secure_key  $Key01
           done
              cat $Deny| sort | uniq -c > $Deny02        
          IP=$( cat $Deny02| awk -F " " '{print $2}' )
               secure_deny_ip
      done
      done
      exit
      

      二期防攻击策略:

      Shell脚本运行的半个月时间里,虽然防止了攻击,但是公司客服反馈有客户被误杀,最严重的是公司有次活动,10秒内发5个短信请求很正常啊,误杀了部分用户,被防火墙禁止IP不能访问任何服务。于是得从nginx应用层找方法,不能用老套方法禁IP了,在网上在找几天的资料解决,几乎没有相同的案例,只能自己创造了。

      天道酬勤,终于有了两个思路:

      一是nginx结合lua来防攻击(在网上看得我云里雾里的,最后不会lua选择放弃这个方案)。

      二是利用ngx_http_referer_module(当时看了2天官网英文资料,http://nginx.org/en/docs/http/ngx_http_referer_module.html,这个页面的让我找到方法,尤其是nginx的if 语句)。

      最终解决思路:

      1、去掉了原来的 拦截ip策略,不载拦截ip。

      2、启用nignx的location 匹配/account 的 h t t p − r e f e r e r 的过滤,当不是正常 http-referer的过滤,当不是正常 http−referer的过滤,当不是正常http-referer,直接在再nginx处理。

      Nginx配置如下:

      location ~ /account (/.*)  {
      if ($http_referer ~  "https://www.xxxxxxxx.net/account/sendPhoneCode" ) {
          #如果匹配就直接返回200,返回404,也行啊,自己定。给可爱的攻击者,不传给后端web
                       return 200;        }
           #不匹配,传给后端web
        proxy_pass  http: //web_group/account/ ;
      }
      

      整个防攻击到现在没有出现任何问题,效果杠杠的。后期会增加第三期,主要是我们NB的开发,从程序级解决,如增加各种验证啊。

      Nginx简单防御CC攻击

      一、主动抑制方法

      为了让Nginx支持更多的并发连接数,根据实际情况对工作线程数和每个工作线程支持的最大连接数进行调整。例如设置“worker_processes 10”和“worker_connections 1024”,那这台服务器支持的最大连接数就是10×1024=10240。

      worker_processes 10;
      events {
      use epoll;
      worker_connections 10240;
      }
      

      Nginx 0.7开始提供了2个限制用户连接的模块:NginxHttpLimitZoneModule和NginxHttpLimitReqModule。NginxHttpLimitZoneModule可以根据条件进行并发连接数控制。

      例如可以定义以下代码:

      http {
      limit_zone   my_zone  $binary_remote_addr  10m;
      server {
      location /somedir/ {
      limit_conn   my_zone  1;
      }
      }
      }
      

      其中“limit_zone my_zone $binary_remote_addr 10m”的意思是定义一个名称为my_zone的存储区域、my_zone中的内容为远程IP地址、my_zone的大小为10M;“location /somedir/”的意思是针对somedir目录应用规则;“limit_conn my_zone 1”的意思是针对上面定义的my_zone记录区记录的IP地址在指定的目录中只能建立一个连接。

      NginxHttpLimitReqModule可以根据条件进行请求频率的控制。例如可以定义以下代码:

      http {
      limit_req_zone  $binary_remote_addr  zone=my_req_zone:10m   rate=1r/s;
      ...
      server {
      ...
      location /somedir/ {
      limit_req_zone   zone= my_req_zone  burst=2;
      }
      

      其中“limit_req_zone $binary_remote_addr zone=my_req_zone:10m rate=1r/s”的意思是定义一个名称为my_req_zone的存储区域,my_req_zone内容为远程IP地址,my_req_zone大小为10M,my_req_zone中的平均请求速率只能为1个每秒;“location /somedir/”的意思是针对somedir目录应用规则;“limit_req_zone zone= my_req_zone burst=2”的意思是针对上面定义的my_req_zone记录区记录的IP地址在请求指定的目录中的内容时最高2个每秒的突发请求速率。

      当有连接触发上诉规则时,Nginx会报“503 Service Temporarily Unavailable”的错误,停止用户请求。返回一个503,对服务器来说影响不大,只占用一个nginx的线程而已,相对来说还是很划算的。

      为了测试效果,我将以上代码放入Nginx的配置文件,并编写了一个PHP文件显示phpinfo;另外还写了一个html文件,其中嵌入了多个iframe调用php文件。当我打开这个html文件了,可以看到只有一个iframe中的php文件正常显示了,其他的iframe都显示503错误。

      应用举例(Discuz!)

      Discuz!是使用比较多的一个php论坛程序。以Discuz!7.0为例,程序目录下有比较多的可以直接访问的php文件,但其中最容易受到攻击的一般有index.php(首页)、forumdisplay.php(板块显示)、viewthread.php(帖子显示)。攻击者一般会对这些页面发起大量的请求,导致HTTP服务器连接数耗尽、mysql数据库停止响应,最终导致服务器崩溃。为了防止上述页面被攻击,我们可以设定以下的规则进行防御:

      http {
      limit_zone   myzone_bbs  $binary_remote_addr  10m;
      limit_req_zone $binary_remote_addr zone=bbs:10m rate=1r/s;
      ...
      server {
      ...
      location ~ ^/bbs/(index|forumdisplay|viewthread).php$ {
      limit_conn   myzone_bbs  3;
      limit_req zone=bbs burst=2 nodelay;
      root           html;
      fastcgi_pass   unix:/dev/shm/php-cgi.sock;
      fastcgi_index  index.php;
      fastcgi_param  SCRIPT_FILENAME  /usr/share/nginx/html$fastcgi_script_name;
      include        fastcgi_params;
      }
      }
      }
      

      应用这条规则后,bbs目录下的index.php、forumdisplay.php和viewthread.php这些页面同一个IP只许建立3个连接,并且每秒只能有1个请求(突发请求可以达到2个)。虽然这样的规则一般来说对正常的用户不会产生影响(极少有人在1秒内打开3个页面),但是为了防止影响那些手快的用户访问,可以在nginx中自定义503页面,503页面对用户进行提示,然后自动刷新。在Nginx中自定义503页面:

      error_page   503   /errpage/503.html;
      

      503页面的源代码:

      
      < head>
      < title>页面即将载入....
      < meta http-equiv=content-type c>
      < META NAME="ROBOTS" C>
      < /head>
      < body bgcolor="#FFFFFF">
      < table cellpadding="0" cellspacing="0" border="0" width="700" align="center" height="85%">
      
      
      

      页面即将载入

      你刷新页面的速度过快。请少安毋躁,页面即将载入...

      [立即重新载入]

      < /table> < /body> < /html>< SCRIPT language=javascript> function update() { window.location.reload(); } setTimeout("update()",2000); < /script>

      二、被动防御方法

      虽然主动防御已经抵挡了大多数HTTP GET FLOOD攻击,但是道高一尺魔高一丈,攻击者会总会找到你薄弱的环节进行攻击。所以我们在这里也要介绍一下被动防御的一些方法。

      封IP地址

      访问者通过浏览器正常访问网站,与服务器建立的连接一般不会超过20个,我们可以通过脚本禁止连接数过大的IP访问。以下脚本通过netstat命令列举所有连接,将连接数最高的一个IP如果连接数超过150,则通过 iptables阻止访问:

      #!/bin/sh
      status=`netstat -na|awk '$5 ~ /[0-9]+:[0-9]+/ {print $5}' |awk -F ":" -- '{print $1}' |sort -n|uniq -c |sort -n|tail -n 1`
      NUM=`echo $status|awk '{print $1}'`
      IP=`echo $status|awk '{print $2}'`
      result=`echo "$NUM > 150" | bc`
      if [ $result = 1 ]
      then
      echo IP:$IP is over $NUM, BAN IT!
      /sbin/iptables -I INPUT -s $IP -j DROP
      fi
      

      运行crontab -e,将上述脚本添加到crontab每分钟自动运行:

      /root/xxxx.sh

      通过apache自带的ab工具进行服务器压力测试:

      ab -n 1000 -c 100 http://www.xxx.com/bbs/index.php

      测试完成后,我们就可以看到系统中有IP被封的提示:

      #tail /var/spool/mail/root
      Content-Type: text/plain; charset=ANSI_X3.4-1968
      Auto-Submitted: auto-generated
      X-Cron-Env: 
      X-Cron-Env: 
      X-Cron-Env: <;PATH=/usr/bin:/bin>
      X-Cron-Env: 
      X-Cron-Env: 
      

      IP:58.246.xx.xx is over 1047, BAN IT!

      至此,又一次HTTP GET FLOOD防御成功。

      根据特征码屏蔽请求(对CC攻击效果较好)

      一般同一种CC攻击工具发起的攻击请求包总是相同的,而且和正常请求有所差异。当服务器遭遇CC攻击时,我们可以快速查看日志,分析其请求的特征,比如User-agent。下面的是某一次CC攻击时的User-agent,Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; MyIE 3.01)Cache-Control: no-store, must-revalidate几乎没有正常的浏览器会在User-agent中带上“must-revalidate”这样的关键字。所以我们可以以这个为特征进行过滤,将User-agent中带有“must-revalidate”的请求全部拒绝访问:

      if ($http_user_agent ~ must-revalidate) {
      return 403;
      }
      

      本文主要介绍了nginx下的HTTP GET FLOOD防御,如果有不对的地方,希望大家可以向我提出。同时,也希望大家能够举一反三,把这种思路应用到apache、lighttpd等常见的web服务器中。