这几天服务器带宽无缘无故升高了很多, 看了一眼nginx日志,发现有很多Baiduspider的请求,百度啥时候这么给力了?
随即在nginx配置中过滤掉了所有带有Baiduspider字样UA的请求,带宽瞬间降低
怀疑网站遭受了伪装成爬虫的CC攻击
在以往的经验中,大家大部分是使用IP段来进行判断的,不过在百度官方文档有提到这么一句
百度蜘蛛IP是不断变的,现在网上的确有一些白名单的说法,暂时是有效的,但不保证今后不会变,所以建议站点还是通过ua进行判断
然而,通过UA判断在现如今的大环境下,已经是杯水车薪,毕竟伪造一个UA,太简单了。
继续查阅官方文档,又发现了下面这一段话。
上周百度站长平台接到某站长求助,表示误封禁了Baiduspider的IP,询问是否有办法获得Baiduspider的所有IP,打算放入白名单加以保护,防止再次误封。在此要告诉各位站长,Baiduspider的IP池是不断变动的,我们无法提供IP全集。
除此之外,之前还有站长发来质疑说Baiduspider光顾过于频繁,已超越服务器承受能力。而百度站长平台追查发现,Baiduspider对该站点的抓取并无异常,那只spider极有可能是个李鬼。
那么,站长该如何通过IP来判断此spider是不是来自百度搜索引擎的呢?可以通过DNS反查方式来解决这个问题。
这里提到了DNS反查,就简单科普一下;一般情况我们都是通过域名去解析IP地址,实际上在支持的条件下,也是可以通过IP去反查到域名的。想了解DNS反查请百度: 反向DNS查询 - T_Tzz的博客
现在先用这两条数据作为测试
1 2 3 |
123.125.71.82 - - [10/Dec/2018:14:08:20 +0800] "GET /video/av2326479 HTTP/1.1" 301 191 "-" "Mozilla/5.0 (Linux;u;Android 4.2.2;zh-cn;) AppleWebKit/534.46 (KHTML,like Gecko) Version/5.1 Mobile Safari/10600.6.3 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)" 168.235.86.231 - - [10/Dec/2018:14:09:29 +0800] "GET /static/js/modern_video.min.js?v=71 HTTP/1.1" 301 191 "http://www.bilibilijj.com/Video/Av25779451" "Mozilla/5.0 (compatible; Baiduspider-render/2.0; +http://www.baidu.com/search/spider.html)" |
从中可以拿到2个IP地址,现在先用123.125.66.120这个IP为例
在终端中键入host 123.125.66.120
得到返回结果120.66.125.123.in-addr.arpa domain name pointer baiduspider-123-125-66-120.crawl.baidu.com.
,那我们可以判定它是一只正常的爬虫
继续用host 168.235.86.231
得到返回结果Host 231.86.235.168.in-addr.arpa. not found: 3(NXDOMAIN)
,好,抓到一只李鬼。
至此,如何判定增加蜘蛛的方式就说完了,接下来就是实现自动批量去检测的方式。
在这里先列出一些我用到的东西以及一些可能出现的问题。
需要用到东西如下
- mongodb
- python3
- iptables(debian 自带的防火墙)
可能面临的问题
- python如何在读取大文件时如何避免内存过高
- python如何执行shell并返回结果
- iptables如何使用
python如何在读取大文件时如何避免内存过高
一般情况下,使用python读取文件大家都是直接读到内存中,譬如data = open('xxx.txt', 'r').read()
,毕竟使用这种方式的时候,往往文件都比较小,也不用担心内存爆炸的问题,然而今天我们需要去读取并遍历一个G级的nginx日志文件,显然这种方式是不可取的,好在在python中有很简单也完美的解决方案
1 2 3 4 5 |
//对可迭代对象file进行迭代,这样会自动的使用buffered IO以及内存管理,这样就不必担心大文件问题了。 with open(nginx_log_path, 'r') as _flie: for line in _flie: print(line) |
python如何执行shell并返回结果
这就是个超级简单的问题了。
1 2 3 4 5 6 |
//执行shell命令并返回0、1 os.system('iptables -I INPUT -s 192.168.0.111 -j DROP') //执行shell命令并返回结果 host_str = os.popen('host 168.235.86.231').read() |
iptables如何使用
说实话,在这里要讲通iptables的所有使用方法,的确是个大难题,毕竟这不是一言两句就能讲通的东西,所以这里就只做最简单的使用介绍。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//显示iptables所有配置 iptables -L //如果使用iptables提示"-bash: iptables: command not found", 请使用whereis iptables查询具体位置一般在/usr/sbin下。 //封锁192.168.0.111对服务器所有接口的访问 iptables -I INPUT -s 192.168.0.111 -j DROP //解除对192.168.0.111的封锁(注意,这里虽然达到了解除的效果,实际上并不是删掉了DROP的记录,而是又新增了一条ACCEPT记录,由于iptables是按照从上往下匹配的,新增的记录会放在第一条,所以ACCEPT过后就解除了,但是这会导致iptables上有多条记录 iptables -I INPUT -s 192.168.0.111 -j ACCEPT //删除指定行数的iptables配置 iptables -D INPUT 11 //注意,这个11是行号,是iptables -L INPUT --line-numbers 所打印出来的行号 |
所有可能列出来的难点都解决了,现在只剩下使用代码来自动化整个过程了, 只要看过上面这些简单的阐述,下面的代码应该也是能够直接读懂的,直接上代码。
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 |
# -*- coding:utf-8 -*- # 过滤假的蜘蛛 import os from pymongo import MongoClient conn = MongoClient('127.0.0.1', 27017) db = conn.spider tb_spider = db.Spilder nginx_log_path = '/opt/verynginx/openresty/nginx/logs/access.log' spider_list = [ ['baiduspider', 'baidu.com.'], ['bingbot', 'msn.com.'], ['googlebot', 'googlebot.com.'], ] ips = [] def find(data, ip): data = data.lower() if ip in ips or tb_spider.find_one({'ip': ip}): return 'EXISTS' ret_str = 'PASS' dic = { 'ip': ip, 'status': 0, } for f in spider_list: if data.find(f[0]) > 0: host_str = os.popen('host %s' % ip).read() if host_str.find(f[1]) == -1: dic['status'] = 1 os.system('iptables -I INPUT -s %s -j DROP' % ip) ret_str = 'DROP' else: ret_str = 'ACCEPT' break tb_spider.insert(dic) ips.append(ip) return ret_str i = 0 with open(nginx_log_path, 'r') as _flie: for line in _flie: i += 1 ip = line.split(' ')[0] result = find(line, ip) print('(%s) %s > %s' % (i, ip, result)) |