背景
之前网站一直没什么安全性的措施(因为我的假设是没人会来看我的网站的……),直到前段时间服务器被某个高一的 dalao D 得实在不行了…… 装了个 Wordfence Security 插件保护 WordPress,但是要全站的保护还是得从 nginx 入手……
于是花了半个下午的时间研究 Nginx 限流功能的配置。
去网上搜索一大堆教程全都是抄来抄去的,找到有点质量的文章真是太不容易了……
之前用的 AppNode 也开过 nginx IP 限流(方便得多……),一分钟内请求超过指定次数就会显示一个静态页面,从而在一定程度上防范 DoS 或者 CC 攻击。但是使用过程中一个很严重的问题就是对搜索引擎不友好,Google、百度都显示抓取失败了。
三种方式
目前判断搜索引擎爬虫的似乎主要有三种方式:
- 存储/维护一个 IP 列表作为白名单 (从这里看到的);
- 通过 UA 判断是否是搜索引擎爬虫;
- 通过反向 DNS 查询(谷歌的建议)。
通过 IP 判断缺点很明显,首先谷歌官方说了人家没有什么爬虫的 IPlist:Verifying Googlebot - Search Console Help
Google doesn't post a public list of IP addresses for webmasters to whitelist. This is because these IP address ranges can change, causing problems for any webmasters who have hard-coded them, so you must run a DNS lookup as described next.
(百度肯定更没有了)
网上去搜索 Google、百度的爬虫 IP 列表,全都是复制来复制去的几年前的内容(吐血)
Github 上逛一圈好像也没找到什么维护 IPlist 的项目,自己维护工作量极大,不太可能……
谷歌推荐的爬虫验证方式是 DNS 反查(rDNS):
- Run a reverse DNS lookup on the accessing IP address from your logs, using the
host
command.- Verify that the domain name is in either googlebot.com or google.com
- Run a forward DNS lookup on the domain name retrieved in step 1 using the
host
command on the retrieved domain name. Verify that it is the same as the original accessing IP address from your logs.
百度的这个文档也说:
站长可以通过 DNS 反查 IP 的方式判断某只 spider 是否来自百度搜索引擎。根据平台不同验证方法不同,如 linux/windows/os 三种平台下的验证方法分别如下:
- 在 linux 平台下,您可以使用
host ip
命令反解 ip 来判断是否来自 Baiduspider 的抓取。Baiduspider 的 hostname 以*.baidu.com
或*.baidu.jp
的格式命名,非*.baidu.com
或*.baidu.jp
即为冒充。
历经千辛万苦找到了 nginx 有一个现成的插件:rDNS
但是 V2EX 上 dalao 们的讨论又泼了盆冷水:如何验证百度蜘蛛
划重点:rDNS 非常消耗资源,而且也可以伪造……
解析记录有 2 种 一种正向 一种反向
反向的意思 就是 IP 解析到域名
这个前提是你要有这个权限 就是有 ASN 并且 IP 是你自己的 或者运营商愿意提供权限(正常情况下是不允许 管理机构有要求)不然你解析什么?
这个和正向一样 也是 DNS 解析记录 只是反过来了
你域名解析到 IP 域名最少你要有管理权限是吧?一个道理
你域名有解析权限了 也可以解析到任意 IP 比如百度?比如谷歌?还是一个道理 反向解析也一样
不过更重要的是:nginx 安装第三方模块是要重新编译整个程序的,太麻烦了……
那么只剩下一种可行的方法:UA 判断。
缺点很明显:很容易伪造!!!在谷歌官方的 UA 列表页面,它给你了一个 Warning:
These values can be spoofed. If you need to verify that the visitor is Googlebot, you should use reverse DNS lookup.
但是至少是一种简单易行的方法……探索了这么长时间就是想水篇博客……
前置知识
推荐一篇博客,介绍 nginx 限流很详细。
nginx 有一个取出 UA 的全局变量 $http_user_agent
。
nginx 配置文件中的 if
语句,条件中比较运算符用法:~
两边字串严格相等;~*
两边字串不分大小写地相等;!~
两边字串严格不相等;!~*
两边字串不区分大小写地不相等。
UA 判断
谷歌官方说它的 UA 列表是:
Overview of Google crawlers (user agents) - Search Console Help
这 UA 也太多了……那就直接检测包含 Googlebot
Baiduspider
子串的好了……
写个 if
判断一下吧:(正则表达式令人头疼)
if ($http_user_agent !~ .*(Googlebot|Baiduspider).*)
{
limit_req zone=reqlim burst=10 nodelay;
}
reload
之后,nginx 会无情地告诉你:
nginx: [emerg] "limit_req" directive is not allowed here in /usr/local/nginx/conf/vhost/skywt.cn.conf:69
「If」 is evil
参见这个页面:If Is Evil | NGINX。「if
不是像你想的那样工作的!」
The only 100% safe things which may be done inside if in a location context are:
Anything else may possibly cause unpredictable behaviour, including potential SIGSEGV.
根据文档里的建议,我们只能写成这种形式:
location /{
error_page 418 = @human;
recursive_error_pages on;
if ($http_user_agent !~ .*(Googlebot|Baiduspider).*)
{
return 418;
}
}
location @human
{
limit_req zone=reqlim burst=10 nodelay;
}
(注意如果装了 wordpress 应该要在 @robots
和 location /
最后加一句 try_files $uri $uri/ /index.php?$args;
)@
开头的路径用于处理内部重定向。
HTTP 418 是「I‘m a teapot」错误(算是 HTTP 协议的一个彩蛋??),所以这么写(应该)不会有影响。
配置并发限制也是类似。
本地测试
用 Apache Benchmark 进行压力测试。
先发 30 个请求,假装我们是搜索引擎爬虫:
ab -n 30 -c 1 -H "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" https://skywt.cn/
返回信息:
Complete requests: 30
Failed requests: 0
表示服务器没有做限流,允许了全部请求。
(通过这个例子可以知道伪造一个 UA 是多么容易(吐血))
再随便搞个 UA 尝试:
ab -n 30 -c 1 -H "User-Agent:Mozilla/5.0 (X11; Linux i686; rv:70.0) Gecko/20100101 Firefox/70.0" https://skywt.cn/
输出:
Complete requests: 30
Failed requests: 15
(Connect: 0, Receive: 0, Length: 15, Exceptions: 0)
Non-2xx responses: 15
15 次请求被拒绝了。
后记
改天还是研究下 rDNS 吧,用了 UA 检测了攻击者还是可以继续攻击……
(况且我还发了篇这么详细的博客讲述我的配置……)
November 7th, 2019 at 09:32 pm
rDNS 太慢的话其实 V2EX 上已经提出了解决方法,缓存下来就可以了。感觉 UA 检测像是自己骗自己
November 7th, 2019 at 09:34 pm
还有 Google 的爬虫是可以运行 JS 的(百度的据说也可以),而我这种菜鸡写的是不资瓷 JS 的,也可以从这个方面入手
November 8th, 2019 at 06:59 am
Nginx 装插件要重新编译主文件,太麻烦了,改天再研究……
话说我国内服务器套个 CloudFlare 不是慢死
话说您能别用别人邮箱评论吗……
November 8th, 2019 at 07:02 am
其实我网站是有静态缓存的……
套个 CDN 的话,国内 CDN 不大方便,而且 ssh 或者连 mc 服务器的时候就不能直接填域名了 qwq
况且我这小网站应该没人会来 DDos 吧……不是分布式的我觉得还扛得住……
December 10th, 2019 at 05:23 pm
Orz SkyWT!
December 10th, 2019 at 05:24 pm
@ Siyuan, Orz Siyuan
December 10th, 2019 at 05:24 pm
@ Darkside, Orz Siyuan
March 30th, 2022 at 10:09 pm
测试评论