一、httpgrard介绍
HttpGuard是基于openresty,以lua脚本语言开发的防cc攻击软件。而openresty是集成了高性能web服务器Nginx,以及一系列的Nginx模块,这其中最重要的,也是我们主要用到的nginx lua模块。HttpGuard基于nginx lua开发,继承了nginx高并发,高性能的特点,可以以非常小的性能损耗来防范大规模的cc攻击。
1.1 httpgrard防cc特效
- 限制访客在一定时间内的请求次数
- 向访客发送302转向响应头来识别恶意用户,并阻止其再次访问
- 向访客发送带有跳转功能的js代码来识别恶意用户,并阻止其再次访问
- 向访客发送cookie来识别恶意用户,并阻止其再次访问
- 支持向访客发送带有验证码的页面,来进一步识别,以免误伤
- 支持直接断开恶意访客的连接
- 支持结合iptables来阻止恶意访客再次连接
- 支持白名单功能
支持根据统计特定端口的连接数来自动开启或关闭防cc模式
详见github地址https://github.com/centos-bz/HttpGuard
另一个README.md地址https://www.centos.bz/forum/thread-119-1-1.html
1.2 安装
shell 脚本安装 ,开启自动防御
#!/bin/sh ############################################################ # cat /etc/redhat-release # CentOS release 6.8 (Final) # uname -r # 2.6.32-642.13.1.el6.x86_64 ############################################################ . /etc/init.d/functions #Defined result function function Msg(){ if [ $? -eq 0 ];then action "$1" /bin/true else action "$1" /bin/false fi } OPENRESTY_VERSION='1.11.2.2' PHP_VERSION='5.5.38' PYTH="/usr/local/openresty/nginx/conf" RED_COLOR='E[1;31m' RES='E[0m' echo -e "$RED_COLOR Transit IP $RES" read -p "Input relay IP: " a function Install_openresty(){ id www&>/dev/null if [ $? -ne 0 ];then useradd -s /sbin/nologin -M www fi yum install -y readline-devel pcre-devel openssl-devel gcc unzip &> /dev/null cd /usr/src/ [ ! -f openresty-${OPENRESTY_VERSION}.tar.gz ] && wget https://openresty.org/download/openresty-1.11.2.2.tar.gz &> /dev/null tar xf openresty-${OPENRESTY_VERSION}.tar.gz cd openresty-${OPENRESTY_VERSION} ./configure --prefix=/usr/local/openresty --with-luajit --with-http_stub_status_module --with-pcre --with-pcre-jit &> /dev/null gmake &> /dev/null && gmake install &> /dev/null [ ! -f master.zip ] && wget --no-check-certificate https://github.com/centos-bz/HttpGuard/archive/master.zip &> /dev/null unzip master.zip &> /dev/null mv HttpGuard-master $PYTH/waf cd $PYTH/waf chown www logs Msg "Install_openresty" } function Configfile_nginx(){ cp $PYTH/nginx.conf $PYTH/nginx.conf.bak >$PYTH/nginx.conf cat >> $PYTH/nginx.conf <<EOF user www; worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; lua_max_running_timers 1; lua_shared_dict guard_dict 100m; lua_shared_dict dict_captcha 70m; init_by_lua_file '/usr/local/openresty/nginx/conf/waf/init.lua'; access_by_lua_file '/usr/local/openresty/nginx/conf/waf/runtime.lua'; lua_package_path "/usr/local/openresty/nginx/conf/waf/?.lua"; sendfile on; keepalive_timeout 65; server { listen 80; server_name localhost; location / { proxy_pass http://${a}; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } } EOF sed -i "s#baseDir = '/data/www/waf/'#baseDir = '$PYTH/waf/'#g" $PYTH/waf/config.lua sed -i '90s#Off#On#' $PYTH/waf/config.lua sed -i '44s#off#On#' $PYTH/waf/config.lua Msg "Configfile_nginx" } function Install_php(){ yum -y install libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel libxml2-devel &> /dev/null cd /usr/src/ [ ! -f php-${PHP_VERSION}.tar.gz ] && wget http://cn.php.net/distributions/php-5.5.38.tar.gz &> /dev/null tar xf php-${PHP_VERSION}.tar.gz cd php-${PHP_VERSION} ./configure --with-png-dir --with-freetype-dir --with-jpeg-dir --with-gd --prefix=/usr/local/php &> /dev/null make &> /dev/null && make install &> /dev/null cd $PYTH/waf/captcha /usr/local/php/bin/php getImg.php &> /dev/null Msg "Install_php" } function Check_openresty(){ /usr/local/openresty/nginx/sbin/nginx -t &> /dev/null /usr/local/openresty/nginx/sbin/nginx if [ `grep "openresty" /etc/rc.local|wc -l` -eq 0 ];then echo "/usr/local/openresty/nginx/sbin/nginx" >>/etc/rc.local fi Msg "Check_openresty" } function Iptables_configure(){ yum -y install ipset &> /dev/null iptables -F /etc/init.d/iptables save &> /dev/null /etc/init.d/iptables restart &> /dev/null ipset create forbidip hash:ip timeout 600 iptables -A INPUT -p tcp -m set --match-set forbidip src -m tcp --dport 80 -j DROP /etc/init.d/iptables save &> /dev/null chkconfig iptables on if [ `grep "forbidip" /etc/rc.local|wc -l` -eq 0 ];then echo "ipset create forbidip hash:ip timeout 600" >> /etc/rc.local fi chkconfig iptables on Msg "Iptables_configure" } function main(){ Install_openresty Configfile_nginx Install_php Check_openresty Iptables_configure } main
防御模块介绍
有三种主动防御模式
-- 主动防御,302响应头跳转模块
-- 主动防御,发送js跳转代码模块
-- 主动防御,发送cookie验证模块
具体查看config.lua配置文件
-- state : 为此模块的状态,表示开启或关闭,可选值为On或Off; -- keySecret : 用于生成token的密码,如果上面的keyDefine为dynamic,就不需要修改 -- 主动防御,发送js跳转代码模块。利用cc控制端无法解析js跳转的特点,来识别是否为正常用户,当有必要时才建议开启。 -- keySecret : 用于生成token的密码,如果上面的keyDefine为dynamic,就不需要修改 -- urlProtect 同limitReqModules模块中的urlProtect的解释。 -- state : 为此模块的状态,表示开启或关闭,可选值为On或Off; -- keySecret : 用于生成token的密码,如果上面的keyDefine为dynamic,就不需要修改 -- urlProtect 同limitReqModules模块中的urlProtect的解释。 -- 自动开启主动防御,原理是根据protectPort端口的已连接数超过maxConnection来确定 -- state : 为此模块的状态,表示开启或关闭,可选值为On或Off; -- interval 间隔30秒检查一次连接数,默认为30秒。 -- protectPort,maxConnection,normalTimes,exceedTimes : enableModule中的模块为关闭状态时,当端口protectPort的连接数连续exceedTimes次超过maxConnection时,开启enableModule中的模块; -- enableModule中的模块为开启状态时,当端口protectPort的连接数连续normalTimes次低于maxConnection时,关闭enableModule中的模块。 -- ssCommand : 我们是使用ss命令来检查特定端口的已连接的连接数,ss命令比同类的命令netstat快得多。请把ss命令的路径改为自己系统上的>路径。 -- enableModules : 自动启动哪个主动防御模块,可选值为redirectModules JsJumpModules cookieModules
测试: -- 值为captcha时,表示ip在黑名单后返回带有验证码的页面,输入正确的验证码才允许继续访问网站
当值为blockAction = "forbidden",-- 值为forbidden时,表示ip在黑名单后,服务器会直接断开与用户的连接.
当值为 blockAction = "iptables",时 cat attack-2016-12-29.log 2016-12-29 00:23:52 [WARNING] [limitReqModules] IP 192.168.179.1 visit 51 times,block it. [root@redis logs]# iptables -L Chain INPUT (policy ACCEPT) target prot opt source destination DROP tcp -- bogon anywhere tcp dpt:http Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination
封锁时间 -- 表示http-guard封锁ip的时间 blockTime = 600, -- JsJumpModules redirectModules cookieModules验证通过后,ip在白名单的时间 whiteTime = 600,
问题点
当这个白名单开启,需在white_ip_list.txt 里写一些IP,否则所有防御失效,这可能是个bug
-- 白名单ip文件,文件内容为正则表达式。
whiteIpModules = { state = "Off", ipList = baseDir.."url-protect/white_ip_list.txt" },
默认只限制php的请求
-- urlProtect : 指定限制请求次数的url正则表达式文件,默认值为.php$,表示只限制php的请求(当然,当urlMatchMode = "uri"时,此正则才>能起作用)
匹配php等其它
.(php|htm|html|asp)$
匹配所有
.*
或者
^/$
.asp.*$
.php.*$
.htm.*$
日志太大
好像关不了,可以加个定时任务清理。cat /dev/null > log
1.3、WAF上线
- 初期上线只记录日志,不开启WAF,防止误杀
- WAF规则管理使用saltstack工具
- 要知道并不是有了WAF就安全,存在人为因素
1.4、Modsecurity
Modsecurity原理分析--从防御方面谈WAF的绕过
http://www.tuicool.com/articles/rE3i63n
安装配置
https://github.com/SpiderLabs/ModSecurity/wiki/Compilation-recipes