debian ubuntu 假蜘蛛硬核过滤方法

这几天服务器带宽无缘无故升高了很多, 看了一眼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的博客

现在先用这两条数据作为测试

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中有很简单也完美的解决方案

//对可迭代对象file进行迭代,这样会自动的使用buffered IO以及内存管理,这样就不必担心大文件问题了。
with open(nginx_log_path, 'r') as _flie:
    for line in _flie:
        print(line)

python如何执行shell并返回结果

这就是个超级简单的问题了。

//执行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的所有使用方法,的确是个大难题,毕竟这不是一言两句就能讲通的东西,所以这里就只做最简单的使用介绍。

//显示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 所打印出来的行号

所有可能列出来的难点都解决了,现在只剩下使用代码来自动化整个过程了, 只要看过上面这些简单的阐述,下面的代码应该也是能够直接读懂的,直接上代码。

# -*- 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))

发表评论

电子邮件地址不会被公开。