网站防刷方案

网站防刷方案

网站重复请求解决方案

摘要

这是讲述如何防止重复请求你的网站, 包括如,爬虫,数据采集,刷排名,批量注册,批量发帖,利用漏洞获取网站数据等等。


目录

  • 1. 访问网站所涉及环节
  • 2. 浏览器款控制方案
  • 3. CDN 与 反向代理
  • 4. 网络设备控制方法
  • 5. 服务器上做控制
    • 5.1. 操作系统部分
    • 5.2. WEB 服务器部分
  • 6. 通过程序控制访问行为
  • 7. 总结

1. 访问网站所涉及环节

简单说就是重复相同的请求

首先看看访问流程所设计的每个环节

		User -> Browse -> CDN/Proxy Cache -> Web Server -> App Server / fastcgi pool -> Cache -> Database		

大部分网站都是这样的结构:用户,浏览器,CDN或反向代理,Web服务器,应用服务器,缓存,数据库

这个访问过程中所涉及的设备

		PC -> ADSL/Cable/Ethernet -> Route -> ... -> Route -> Firewall -> Load Balance -> Switch -> Server		

我们看看从那些环节可以截获用户的刷新行为

可控制环节

  1. CDN / 反向代理,提供一些基本防护功能,过于简单。
  2. 3/4层设备,防火墙/路由器/交换机,主要还是靠防火墙设备,例如Cisco ASA 系列防火墙,都提供IPS/IDS服务(需要单独采购,设备默认没有)主要是针对IP地址的请求频率做出策略控制,
  3. 4/7 层负载均衡设备, 一半负载均衡设备都附带此功能。但不是他主要的功能,没有能力购买防火墙设备的中小公司可以使用该功能,7层功能非常强大,但都是通用功能,不一定满足你的个性化需求。
  4. 浏览器,这是主要是改变浏览器端设置,利用Cookie变化,Javascript等技术,阻止重复请求
  5. WEB 服务器,在web上通过扩展模块与相应的配置也能达到一定的效果
  6. 应用服务器, 主要是通过编写程序在阻止恶意访问。

依次从上至下,越能提前在上一层阻止行为越好,否则就在下一层截获。

2. 浏览器款控制方案

通过 Javascript 防止重复点击提交按钮,通常的做法是将按钮禁用 通过 disabled 属性实现。下面是Jquery例子

$("form").submit(function(){
  $(":submit",this).attr("disabled","disabled");
});		

在上面的例子基础上可以改良,增加计时器,限制一定时间内不可重复提交。

通过 Cookie技术控制重复访问动作

		访问第一个页面 login.example.com/form.ext 的时候设置一个 cookie 变量
访问第二个页面 login.example.com/auth.ext 的时候判断上一个页面设置的 cookie 是否有效,如果无效拒绝访问。		

可以进一步增加难度,例如用户注册分为很多步骤,每一个步骤都会设置一个标记,如果用户行为不是按照顺序访问,直接在最后一个页面提交,明显可以判断是非法行为。

这里的方案是针对人工操作,更多的时采用程序实现刷新,采集,爬虫等等。

3. CDN 与 反向代理

CDN 都提供一些基本的防护功能,主要是针对 IP 地址, URL 做一些限制

如果自己做反向代理,控制权更大,可以充分使用操作系统带的包过滤防火墙与代理软件所提供的7层功能

由于很多web server 具备代理服务器功能,配置也相差无几,所有在后面web server 会详细介绍。

4. 网络设备控制方法

每一个网络设备使用方法都不同,这里无法举例,但原理都是相同的。

3/4 层网络设备可以按照IP地址与端口号访问情况做具体限制,如单位时间内允许的访问次数,这种对于大量的攻击比较有效

7层网络设备功能非常强大,就可以根据HTTP头做规则策略,如限制URL的单位时间访问的IP数量,判断 Cookie 等信息,

5. 服务器上做控制

这部分分为,操作系统与web服务器两个部分

5.1. 操作系统部分

操作系统部分,主要是通过linux内核提供的包过滤功能,通常所说的iptables

iptables -A INPUT -p icmp -m limit --limit 3/s -j LOG --log-level INFO --log-prefix "ICMP packet IN: "

iptables -N syn-flood
iptables -A INPUT -p tcp --syn -j syn-flood
iptables -I syn-flood -p tcp -m limit --limit 3/s --limit-burst 6 -j RETURN
iptables -A syn-flood -j REJECT			

限制源IP的访问数量

-A INPUT -p tcp -m state --state NEW -m tcp --dport 80 --tcp-flags FIN,SYN,RST,ACK SYN -m connlimit --connlimit-above 50 --connlimit-mask 32 -j REJECT --reject-with icmp-port-unreachable
-A INPUT -p tcp -m state --state NEW -m tcp --dport 443 --tcp-flags FIN,SYN,RST,ACK SYN -m connlimit --connlimit-above 50 --connlimit-mask 32 -j REJECT --reject-with icmp-port-unreachable			

关键字,字符串过略

			iptables -A INPUT -p tcp --dport 80 -m string --algo bm --string "XXDD0S" -j DROP			

以上所讲都是被动方法,需要系统管理一条一条添加规则。

提示

基于IP与端口的方法有明显的不足,经常会误将某些正常的IP地址封锁。

下面通过脚本实现主动防御,通过提取 access.log 文件定位更精准,同时实现了黑/白名单可以将安全IP放置在白名单中。

			#!/bin/bash
########################################
# Homepage: http://netkiller.github.io
# Author: neo <netkiller@msn.com>
########################################
PIPE=/tmp/pipe
pidfile=/tmp/firewall.pid

ACCCESS_LOG=/tmp/access.log
TIMEPOINT='24/May/2012'
BLACKLIST=/var/tmp/black.lst
WHITELIST=/var/tmp/white.lst
########################################

if [ -z "$( egrep "CentOS|Redhat" /etc/issue)" ]; then
	echo 'Only for Redhat or CentOS'
	exit
fi

if [ ! -f ${BLACKLIST} ]; then
    touch ${BLACKLIST}
fi

if [ ! -f ${WHITELIST} ]; then
    touch ${WHITELIST}
fi

for deny in $(grep ${TIMEPOINT} ${ACCCESS_LOG} | awk '{print $1}' | awk -F'.' '{print $1"."$2"."$3"."$4}' | sort | uniq -c | sort -r -n | head -n 30| awk '{print $2}')
do

    if [ $(grep -c $deny ${WHITELIST}) -ne 0 ]; then
        echo 'Allow IP:' $deny
	iptables -D INPUT -p tcp --dport 443 -s $deny -j DROP
	iptables -D INPUT -p tcp --dport 80 -s $deny -j DROP
	continue
    fi

    if [ $(grep -c $deny ${BLACKLIST}) -eq 0 ] ; then

	echo 'Deny IP:' $deny
        echo $deny >> ${BLACKLIST}
        iptables -I INPUT -p tcp --dport 443 -s $deny -j DROP
        iptables -I INPUT -p tcp --dport 80 -s $deny -j DROP
    fi
done			

相比前面脚本,这个脚本更高级,实现关键字过滤,管道实时处理,这样不回因为日志尺寸变大,影响到脚本的处理性能。

			#!/bin/bash
########################################
# Homepage: http://netkiller.github.io
# Author: neo <netkiller@msn.com>
########################################
ACCESSLOG=/www/logs/www.example.com/access.$(date +'%Y-%m-%d').log
TIMEPOINT='24/May/2012'
KEYWORD=send.php
BLACKLIST=/var/tmp/black.lst
WHITELIST=/var/tmp/white.lst
PIPE=/var/tmp/pipe
pidfile=/var/tmp/firewall.pid
logfile=/var/tmp/firewall.log
########################################
if [ -z "$( egrep "CentOS|Redhat" /etc/issue)" ]; then
	echo 'Only for Redhat or CentOS'
	exit
fi

if [ -z $1 ]; then
    echo "$0 clear|fw|collect|process|close"
fi

if [ "$1" == "clear" ]; then
    rm -rf $BLACKLIST
    rm -rf $PIPE
    echo "Clear OK!!!"
fi

if [ "$1" == "close" ]; then
	killall tail
    kill `cat $pidfile`
    echo > $pidfile
fi

if [ ! -e $PIPE ]; then
    mkfifo $PIPE
fi

if [ "$1" == 'fw' ]; then
    iptables -A OUTPUT -p tcp --dport 2049 -j REJECT
    iptables -A OUTPUT -p tcp -m multiport --dports 22,21 -j REJECT

	for ipaddr in ${WHITELIST}
	do
		if [ $(grep -c $ipaddr ${WHITELIST}) -ne 0 ]; then
			iptables -A INPUT -p tcp --dport 443 -s $ipaddr -j ACCEPT
			iptables -A INPUT -p tcp --dport 80 -s $ipaddr -j ACCEPT
			echo 'Allow IP:' $ipaddr >> $logfile
		fi
		if [ $(grep -c $ipaddr ${BLACKLIST}) -eq 0 ] ; then
			iptables -D INPUT -p tcp --dport 443 -s $ipaddr -j DROP
			iptables -D INPUT -p tcp --dport 80 -s $ipaddr -j DROP
			echo 'Deny IP:' $ipaddr

		fi
	done

fi

if [ "$1" == "collect" ]; then
    killall tail
    for (( ; ; ))
    do
        tail -f $ACCESSLOG | grep $KEYWORD | cut -d ' ' -f1 > $PIPE
    done &
    echo $! > $pidfile
fi

if [ "$1" == "process" ]; then

	if [ ! -f $BLACKLIST ]; then
		touch $BLACKLIST
	fi

	if [ ! -f ${WHITELIST} ]; then
		touch ${WHITELIST}
	fi

	for (( ; ; ))
	do
		while read ipaddr
		do
			if [ $(grep -c $ipaddr ${WHITELIST}) -ne 0 ]; then
				echo 'Allow IP:' $ipaddr >> $logfile
				continue
			fi

			grep $ipaddr ${BLACKLIST}
			if [ $? -eq 1 ] ; then
				echo $ipaddr >> ${BLACKLIST}
				iptables -I INPUT -p tcp --dport 80 -s $ipaddr -j DROP
				echo "Deny IP: $ipaddr" >> $logfile
			fi
		done < $PIPE
	done &
	echo $! >> $pidfile
fi			

5.2. WEB 服务器部分

下面所讲技术,适用于反向代理,负载均衡,web服务器

Web 服务器也可以实现前面所说的防火墙等设备3/4层的功能,同时具备七层功能,很多负载均衡设备7层采用web服务器实现,例如 F5 7层的高级功能是由 Apache httpd 来完成(apache 是经过二次开发的), 所以7层的部门我们主要在这里深入讨论

如果你有防火墙设备应该首先考虑在防火墙端做控制,如果没有防火墙那麽就考虑在负载均衡设备中做控制,这些设备你都没有,最后考虑在反向代理中处理,最后考虑web服务器。

限制IP地址在这里可以做到更细腻的控制,例如实现某个目录的,某URL的IP访问策略。请自行查找手册或参考《Netkiller Web 手札》

HTTP 协议头

我们要做以下几种限制

  1. 限制 http_referer, 常说的防盗链。
  2. 限制 http_user_agent, 主要是防爬虫
  3. 限制 request_method, 不是所有页面都允许 POST
  4. 限制 http_cookie, 没有携带正确的 cookie 不允许访问

上面7层访问控制还是比较粗糙的,主要是给应用程序减压,更细腻的控制需要通过程序手段,实现更智能判断。 不过同上上面的层层限制,已经足矣改善你的状况,如果还是无效继续往下看。

			valid_referers none blocked *.example.com example.com;
if ($invalid_referer) {
	#rewrite ^(.*)$  http://www.example.com/cn/$1;
	return 403;
}		
if ($http_user_agent = "") { 
	return 403; 
}		

6. 通过程序控制访问行为

设计应用防火墙,将所有资源纳入管理范围

IP地址,上面已经反复强调怎样封锁IP地址,但都过于粗糙,很多时候是一刀切。在程序中实现禁止IP访问,更灵活

我们要做以下几种限制

  1. 单位时间内访问次数
  2. 访问时间间隔设置
  3. 封锁时间设置
  4. 黑白名单

验证码,最常用的,最有效的方法,分为图片扭曲法,问提/答案 法,手机验证码,语音验证码等等方法,形式多重多样

http_referer, 虽然上面已经做了 http_referer 限制,但是web 服务器只能做粗糙限制,允许 *.example.com 域进行访问,但我需要更精确的控制。例如:

		www.example.com (此时 http_referer 为空,或者其他,这不重要) -> login.example.com (http_referer: www.example.com)-> login.example.com/auth.ext (http_referer: login.example.com) -> login.example.com/secussed.ext (http_referer: login.example.com/auth.ext)		  

看明白了吗 http_referer 每次都是上一个页面,我们程序中判断,如果上一个页面不是我们所指定的,或者不再允许列表内,就拒绝访问

request_method

		www.example.com (GET) -> login.example.com (GET)-> login.example.com/auth.ext (POST) -> login.example.com/secussed.ext (GET)		

同理,在不允许的页面POST操作,将立即拒绝

http_cookie

www.example.com (cookie 1) -> login.example.com (cookie 2)-> login.example.com/auth.ext (cookie 3) -> login.example.com/secussed.ext (cookie 4)		

没有按照指定流程访问,cookie 值不会变化,属于异常行为

cookie + redis 间隔时间

提示

上面所有的操作都将计入日志,通过脚本可以将异常访问行文达到一定次数后,放入iptables DROP链中。

7. 总结

上面提方法单一使用过于简单,需要组合使用,同时经常调整组合方式才能更有效阻止各种良性与恶性网站访问行为。

原文发布于微信公众号 - Netkiller(netkiller-ebook)

原文发表时间:2015-09-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FreeBuf

打造一款属于自己的远程控制软件(一)

本人为了工作中便于管理手中大量的计算机一直在寻找一款合适的远程控制软件。鉴于网上下载的远程控制软件大多都被不同程度地植入后门,于是萌生了自己打造一款远控的想法,...

5388
来自专栏腾讯Bugly的专栏

【Dev Club 分享】微信mars 的高性能日志模块 xlog

Dev Club 是一个交流移动开发技术,结交朋友,扩展人脉的社群,成员都是经过审核的移动开发工程师。每周都会举行嘉宾分享,话题讨论等活动。 本期,我们邀请了 ...

4915
来自专栏IT大咖说

vSAN常见错误故障排错

内容来源:2018 年 8 月 7 日,VMware大中华区原厂高级技术讲师史峻在“VMware直播分享 第二期”进行《vSAN常见错误故障排错》演讲分享。IT...

703
来自专栏我是攻城师

关于SparkStreaming中的checkpoint

2624
来自专栏派森公园

docker的reap问题

在使用docker容器的时候,应该了解“PID1僵尸进程reap”问题。如果使用的时候不加注意,可能会导致出现一些意想不到的问题。

883
来自专栏一只程序汪的自我修养

可能是一份没什么用的爬虫代理IP指南

1523
来自专栏java达人

防止表单重复提交的思路和方法

作为一个软件开发者,绝不能奢望你的用户会规规矩矩地使用你的软件,他们一般都是缺乏耐心,“胡作非为”的。比如当他点击提交表单时,服务器处理比较慢, 页面上没有任何...

1798
来自专栏惨绿少年

企业防火墙之iptables

1.1 企业中安全优化配置原则 尽可能不给服务器配置外网ip ,可以通过代理转发或者通过防火墙映射.并发不是特别大情况有外网ip,可以开启防火墙服务. 大并发的...

4876
来自专栏数据和云

【新书连载】一波三折:释放内存导致数据库崩溃

编辑说明:《Oracle性能优化与诊断案例精选》出版以来,收到很多读者的来信和评论,我们会通过连载的形式将书中内容公布出来,希望书中内容能够帮助到更多的读者朋友...

3548
来自专栏Golang语言社区

在Go程序中实现服务器重启的方法

Go被设计为一种后台语言,它通常也被用于后端程序中。服务端程序是GO语言最常见的软件产品。在这我要解决的问题是:如何干净利落地升级正在运行的服务端程序。 目标:...

3077

扫描关注云+社区