-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrefresh.xml
390 lines (361 loc) · 15.1 KB
/
refresh.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
<?xml version="1.0" encoding="UTF-8"?>
<!-- $Id$ -->
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V5.0//EN"
"/usr/share/xml/docbook/schema/dtd/5.0/docbook.dtd" [
<!ENTITY article.author.xml SYSTEM "../common/article.author.xml">
<!ENTITY book.info.legalnotice.xml SYSTEM "../common/book.info.legalnotice.xml">
<!ENTITY book.info.abstract.xml SYSTEM "../common/book.info.abstract.xml">
]>
<article xml:base="http://netkiller.sourceforge.net/journal/"
xmlns="http://docbook.org/ns/docbook" xml:lang="zh-cn">
<articleinfo>
<title>网站防刷方案</title>
<subtitle>网站重复请求解决方案</subtitle>
&article.author.xml;
&book.info.legalnotice.xml;
<abstract>
<para>这是讲述如何防止重复请求你的网站, 包括如,爬虫,数据采集,刷排名,批量注册,批量发帖,利用漏洞获取网站数据等等。</para>
</abstract>
&book.info.abstract.xml;
<keywordset>
<keyword>刷新, 采集, 爬虫</keyword>
<keyword>CC攻击, 洪水攻击</keyword>
<keyword></keyword>
</keywordset>
</articleinfo>
<section>
<title>访问网站所涉及环节</title>
<para>简单说就是重复相同的请求</para>
<!--
<orderedlist>
<title>行为分类,刷网站,可以分为下面几种类型</title>
<listitem>
<para>恶意攻击,具有攻击性质,</para>
</listitem>
<listitem>
<para>获得利益,从你的网站上获取资源,或者 </para>
</listitem>
<listitem>
<para>系统支持,部署工程师</para>
</listitem>
</orderedlist>
-->
<para>首先看看访问流程所设计的每个环节</para>
<screen>
<![CDATA[
User -> Browse -> CDN/Proxy Cache -> Web Server -> App Server / fastcgi pool -> Cache -> Database
]]>
</screen>
<para>大部分网站都是这样的结构:用户,浏览器,CDN或反向代理,Web服务器,应用服务器,缓存,数据库</para>
<para>这个访问过程中所涉及的设备</para>
<screen>
<![CDATA[
PC -> ADSL/Cable/Ethernet -> Route -> ... -> Route -> Firewall -> Load Balance -> Switch -> Server
]]>
</screen>
<para>我们看看从那些环节可以截获用户的刷新行为</para>
<orderedlist>
<title>可控制环节</title>
<listitem>
<para>CDN / 反向代理,提供一些基本防护功能,过于简单。</para>
</listitem>
<listitem>
<para>3/4层设备,防火墙/路由器/交换机,主要还是靠防火墙设备,例如Cisco ASA 系列防火墙,都提供IPS/IDS服务(需要单独采购,设备默认没有)主要是针对IP地址的请求频率做出策略控制,</para>
</listitem>
<listitem>
<para>4/7 层负载均衡设备, 一半负载均衡设备都附带此功能。但不是他主要的功能,没有能力购买防火墙设备的中小公司可以使用该功能,7层功能非常强大,但都是通用功能,不一定满足你的个性化需求。</para>
</listitem>
<listitem>
<para>浏览器,这是主要是改变浏览器端设置,利用Cookie变化,Javascript等技术,阻止重复请求</para>
</listitem>
<listitem>
<para>WEB 服务器,在web上通过扩展模块与相应的配置也能达到一定的效果</para>
</listitem>
<listitem>
<para>应用服务器, 主要是通过编写程序在阻止恶意访问。</para>
</listitem>
</orderedlist>
<para>依次从上至下,越能提前在上一层阻止行为越好,否则就在下一层截获。</para>
</section>
<section id="browse">
<title>浏览器款控制方案</title>
<para>通过 Javascript 防止重复点击提交按钮,通常的做法是将按钮禁用 通过 disabled 属性实现。下面是Jquery例子</para>
<screen>
$("form").submit(function(){
$(":submit",this).attr("disabled","disabled");
});
</screen>
<para>在上面的例子基础上可以改良,增加计时器,限制一定时间内不可重复提交。</para>
<para>通过 Cookie技术控制重复访问动作</para>
<screen>
<![CDATA[
访问第一个页面 login.example.com/form.ext 的时候设置一个 cookie 变量
访问第二个页面 login.example.com/auth.ext 的时候判断上一个页面设置的 cookie 是否有效,如果无效拒绝访问。
]]>
</screen>
<para>可以进一步增加难度,例如用户注册分为很多步骤,每一个步骤都会设置一个标记,如果用户行为不是按照顺序访问,直接在最后一个页面提交,明显可以判断是非法行为。</para>
<para>这里的方案是针对人工操作,更多的时采用程序实现刷新,采集,爬虫等等。</para>
</section>
<section id="proxy">
<title>CDN 与 反向代理</title>
<para>CDN 都提供一些基本的防护功能,主要是针对 IP 地址, URL 做一些限制</para>
<para>如果自己做反向代理,控制权更大,可以充分使用操作系统带的包过滤防火墙与代理软件所提供的7层功能</para>
<para>由于很多web server 具备代理服务器功能,配置也相差无几,所有在后面web server 会详细介绍。</para>
</section>
<section id="firewall">
<title>网络设备控制方法</title>
<para>每一个网络设备使用方法都不同,这里无法举例,但原理都是相同的。</para>
<para>3/4 层网络设备可以按照IP地址与端口号访问情况做具体限制,如单位时间内允许的访问次数,这种对于大量的攻击比较有效</para>
<para>7层网络设备功能非常强大,就可以根据HTTP头做规则策略,如限制URL的单位时间访问的IP数量,判断 Cookie 等信息, </para>
</section>
<section>
<title>服务器上做控制</title>
<para>这部分分为,操作系统与web服务器两个部分</para>
<section id="os">
<title>操作系统部分</title>
<para>操作系统部分,主要是通过linux内核提供的包过滤功能,通常所说的iptables</para>
<screen>
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
</screen>
<para>限制源IP的访问数量</para>
<screen>
-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
</screen>
<para>关键字,字符串过略</para>
<screen>
<![CDATA[
iptables -A INPUT -p tcp --dport 80 -m string --algo bm --string "XXDD0S" -j DROP
]]>
</screen>
<para>以上所讲都是被动方法,需要系统管理一条一条添加规则。</para>
<tip>
<para>基于IP与端口的方法有明显的不足,经常会误将某些正常的IP地址封锁。</para>
</tip>
<para>下面通过脚本实现主动防御,通过提取 access.log 文件定位更精准,同时实现了黑/白名单可以将安全IP放置在白名单中。</para>
<screen>
<![CDATA[
#!/bin/bash
########################################
# Homepage: http://netkiller.github.io
# Author: neo <[email protected]>
########################################
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
]]>
</screen>
<para>相比前面脚本,这个脚本更高级,实现关键字过滤,管道实时处理,这样不回因为日志尺寸变大,影响到脚本的处理性能。</para>
<screen>
<![CDATA[
#!/bin/bash
########################################
# Homepage: http://netkiller.github.io
# Author: neo <[email protected]>
########################################
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
]]>
</screen>
</section>
<section id="server">
<title>WEB 服务器部分</title>
<para>下面所讲技术,适用于反向代理,负载均衡,web服务器</para>
<para>Web 服务器也可以实现前面所说的防火墙等设备3/4层的功能,同时具备七层功能,很多负载均衡设备7层采用web服务器实现,例如 F5 7层的高级功能是由 Apache httpd 来完成(apache 是经过二次开发的), 所以7层的部门我们主要在这里深入讨论</para>
<para>如果你有防火墙设备应该首先考虑在防火墙端做控制,如果没有防火墙那麽就考虑在负载均衡设备中做控制,这些设备你都没有,最后考虑在反向代理中处理,最后考虑web服务器。</para>
<para>限制IP地址在这里可以做到更细腻的控制,例如实现某个目录的,某URL的IP访问策略。请自行查找手册或参考《Netkiller Web 手札》</para>
<para>HTTP 协议头</para>
<orderedlist>
<title>我们要做以下几种限制</title>
<listitem>
<para>限制 http_referer, 常说的防盗链。</para>
</listitem>
<listitem>
<para>限制 http_user_agent, 主要是防爬虫</para>
</listitem>
<listitem>
<para>限制 request_method, 不是所有页面都允许 POST</para>
</listitem>
<listitem>
<para>限制 http_cookie, 没有携带正确的 cookie 不允许访问</para>
</listitem>
</orderedlist>
<para>上面7层访问控制还是比较粗糙的,主要是给应用程序减压,更细腻的控制需要通过程序手段,实现更智能判断。 不过同上上面的层层限制,已经足矣改善你的状况,如果还是无效继续往下看。</para>
<screen>
<![CDATA[
valid_referers none blocked *.example.com example.com;
if ($invalid_referer) {
#rewrite ^(.*)$ http://www.example.com/cn/$1;
return 403;
}
]]>
</screen>
<screen>
if ($http_user_agent = "") {
return 403;
}
</screen>
</section>
</section>
<section>
<title>通过程序控制访问行为</title>
<para>设计应用防火墙,将所有资源纳入管理范围</para>
<para>IP地址,上面已经反复强调怎样封锁IP地址,但都过于粗糙,很多时候是一刀切。在程序中实现禁止IP访问,更灵活</para>
<orderedlist>
<title>我们要做以下几种限制</title>
<listitem>
<para>单位时间内访问次数</para>
</listitem>
<listitem>
<para>访问时间间隔设置</para>
</listitem>
<listitem>
<para>封锁时间设置</para>
</listitem>
<listitem>
<para>黑白名单</para>
</listitem>
</orderedlist>
<screen>
</screen>
<para>验证码,最常用的,最有效的方法,分为图片扭曲法,问提/答案 法,手机验证码,语音验证码等等方法,形式多重多样</para>
<para>http_referer, 虽然上面已经做了 http_referer 限制,但是web 服务器只能做粗糙限制,允许 *.example.com 域进行访问,但我需要更精确的控制。例如:</para>
<screen>
<![CDATA[
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)
]]>
</screen>
<para>看明白了吗 http_referer 每次都是上一个页面,我们程序中判断,如果上一个页面不是我们所指定的,或者不再允许列表内,就拒绝访问</para>
<para>request_method</para>
<screen>
<![CDATA[
www.example.com (GET) -> login.example.com (GET)-> login.example.com/auth.ext (POST) -> login.example.com/secussed.ext (GET)
]]>
</screen>
<para>同理,在不允许的页面POST操作,将立即拒绝</para>
<para>http_cookie</para>
<screen>
www.example.com (cookie 1) -> login.example.com (cookie 2)-> login.example.com/auth.ext (cookie 3) -> login.example.com/secussed.ext (cookie 4)
</screen>
<para>没有按照指定流程访问,cookie 值不会变化,属于异常行为 </para>
cookie + redis
间隔时间
<tip>
<para>上面所有的操作都将计入日志,通过脚本可以将异常访问行文达到一定次数后,放入iptables DROP链中。</para>
</tip>
</section>
<section>
<title>总结</title>
<para>上面提方法单一使用过于简单,需要组合使用,同时经常调整组合方式才能更有效阻止各种良性与恶性网站访问行为。</para>
</section>
</article>