在查看网站日志时,会发下有许多的恶意扫描和垃圾爬虫,为了网站安全和减轻服务器压力,我们应该想办法阻止这些垃圾请求。

具体操作就是,先通过 Nginx 访问日志,查看哪些是垃圾请求,然后通过 Nginx 配置,屏蔽他们访问。

本人网站的 Nginx Access 日志在:/var/log/nginx/ 目录。

$ nginx -v
nginx version: nginx/1.18.0

屏蔽恶意IP地址

(1)使用 awk 命令查询访问最频繁的IP:

$ awk '{print $1}' access.log|sort | uniq -c |sort -n -k 1 -r|more

说明,使用 awk 命令拆分日志,打印第一列($1表示第一列,默认的 access日志,第一列是IP地址),排序和去重后查看。

(2)使用 deny 指令屏蔽 IP

获取到恶意访问的IP后,我们可以使用 deny 指令来屏蔽。

可以指定单个IP,也可以按照IP段进行屏蔽。以下是屏蔽示例:

http {

    deny 108.179.194.35; # 扫描 wordpress 
    deny 159.203.31.171; # 扫描 wp-login
    deny 158.69.243.0/24;  # MJ12bot
    deny 46.229.168.0/24;  # SemRush 
    deny 54.36.148.0/24;   # AhrefsBot 
    deny 54.36.149.0/24;   # AhrefsBot
}

备注: deny 指令可以在 Nginx 的 http, server, location 上下文中指定。

(3)另外还有一个 allow 指令,指明哪些请求可以访问。

对于仅限特定用户访问的内容,我们可以指明其允许访问的IP。比如网站管理后台的链接,仅限指定的ip段访问。

location /admin {
    allow 192.168.1.0/24;
    allow 112.66.77.88;
    allow 112.65.12.0/24;
    deny  all;
}

说明,这里配置的,除了指定的几个ip 或 ip段,剩下的全部拒绝访问。

根据客户端的请求链接屏蔽访问

(1)使用 awk 命令查询访问最频繁的链接

$ awk '{print $7}' access.log|sort | uniq -c |sort -n -k 1 -r|more

说明,本人网站的 access.log 第7列为 url 地址(Nginx默认配置),如果指明了nginx日志格式,可能会有不同。

(2)找出这些恶意的访问地址

这里列出一些访问比较多的恶意链接

/wp-signin.php?dizo&ping
/wp-login.php
/wp-includes/css/wp-config.php
/wp-includes/wlwmanifest.xml
/.env
/phpMyAdmin-2.6.3/
/phpMyAdmin-2.6.2/
/root.rar
/bak.zip
/wang123.net1.rar
/wang123.net1.zip
/wang123.net2.rar
/wang123.net2.zip
/inc/config.asp
/config/AspCms_Config.asp
/.aws/.credentials.swp
/.aws/config

可以看到这是在扫描 WordPress 和 phpMyAdmin、下载网站的压缩包、扫描网站可能存在的配置。

在 Nginx 中,我们可以使用 $document_uri 来获取到请求 uri,根据 uri 我们来判断哪些后缀的请求我们拒绝其访问。

(3)现根据后缀名屏蔽掉一批请求:

# 屏蔽恶意访问
if ($document_uri ~* \.(php|asp|aspx|jsp|swp|git|env|yaml|yml|sql|db|bak|ini|docx|doc|rar|tar|gz|zip|log)$) {
    return 404;
}

说明,这里使用 if 语句,匹配以 . 开始,以 php、asp 、jsp 、env 等后缀结尾的文件,如果存在,nginx 则直接返回 404 给客户端。

注意,这里的配置的后缀仅供参考,按需配置,比如你如果想让用户下载doc文件,就把 doc|docx 去掉。

Tips: 变量 $document_uri ($uri) 与 $request_uri 有什么不同?

比如访问链接是 https://wang123.net/posts/test.db?wd=test&limit=5

$document_uri 变量的值为:/posts/test.db

$request_uri 变量的值为: /posts/test.db?wd=test&limit=5

(3)在根据关键词屏蔽一批请求:

对于一些我们不能按照后缀进行屏蔽的链接,比如 /wp-includes/wlwmanifest.xml ,这个 xml 后缀不能屏蔽(因为网站其他地方有要用的xml链接,因此我们改用其他关键词进行屏蔽。

根据请求 uri 里的的关键词进行屏蔽,示例如下:

if ($document_uri ~* (wordpress|phpinfo|wlwmanifest|phpMyAdmin|xmlrpc)) {
    return 404;
}

说明,这里匹配几个特定关键词,匹配上来直接返回 404 错误。再访问 /wp-includes/wlwmanifest.xml 会直接 404。

注意,这里的配置要谨慎,如果配置了,url链接里就不能有这些关键词了,否则 nginx 则直接返回 404 错误。

屏蔽恶意爬虫

浏览器或者爬虫工具访问,一般都会带有 User Agent (浏览器用户标识)。

我们根据一些关键词,屏蔽特定的爬虫或工具。

在 Ngxin 中,$http_user_agent 变量可以获取到 用户的 UA ,匹配到我们指定的关键词,直接返回 403 无权访问。

server {
    # 禁止Scrapy等工具的抓取
    if ($http_user_agent ~* (Scrapy|HttpClient|PostmanRuntime|ApacheBench|Java||python-requests|Python-urllib|node-fetch)) {
        return 403;
    }

    # 禁止指定的 user agent 以及为空的访问
    if ($http_user_agent ~ "Nimbostratus|MJ12bot|MegaIndex|YisouSpider|^$" ) {
        return 403;             
    }

}

注意,这里仅列出来了部分爬虫的 UA 关键词,更多的可以去网上搜索“垃圾爬虫”。

有些爬虫也支持 robots 协议, 因为我们在网站根目录的 robots.txt 中也可以都加上。

User-agent:*
Disallow:/dashboard
Disallow:/admin
Disallow:/static
Disallow:/assets

User-Agent: AhrefsBot
Disallow: /
User-Agent: MJ12bot
Disallow: /
User-Agent: DotBot
Disallow: /
User-Agent: SemrushBot
Disallow: /
User-Agent: MauiBot
Disallow: /

对IP和服务进行限流

​ Nginx 提供了2个模块来处理限流,分别是 ngx_http_limit_conn_module 和 ngx_http_limit_req_module 模块

这里我们使用 ngx_http_limit_conn_module 里的 limit_conn_zone 和 limit_conn 指令配置,限制请求的连接数。

http {
    ##### limit
    limit_conn_zone $binary_remote_addr zone=perIP:10m;
    limit_conn_zone $server_name zone=perServer:10m;
    limit_conn perIP 10;
    limit_conn perServer 200;
    limit_conn_status 503;
}

limit_conn_zone 配置存在的连接的空间(zone),一个名字叫 perIP,一个名字叫 perServer,空间的大小都指定大小为 10m 。

limit_conn perip 10 作用的 key 是 $binary_remote_addr,表示限制单个IP同时最多能持有10个连接。

limit_conn perserver 200 作用的 key 是 $server_name,表示虚拟主机(server) 同时能处理并发连接的总数为 200。

limit_conn_status 表示,如果超出链接,直接返回 503 错误。

注意, limit_conn 的数值按需调整。上边的配置会对全站都生效。如果只是想对某个 uri 限流,可以将 limit_conn 写在 location 上下文里。

http {
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    
    server {
        location /download/ {
            limit_conn addr 1;
        }
    }
}

总结

为了方便管理,我们创建两个配置文件,对ip和spider分别管理。

屏蔽IP访问 block_ips.conf

deny 108.179.194.35;   # 扫描 wordpress 
deny 159.203.31.171;   # 扫描 wp-login
deny 158.69.243.0/24;  # MJ12bot
deny 46.229.168.0/24;  # SemRush 
deny 54.36.148.0/24;   # AhrefsBot 
deny 54.36.149.0/24;   # AhrefsBot

屏蔽UA访问 block_spiders.conf

# 禁止Scrapy等工具的抓取
if ($http_user_agent ~* (Scrapy|HttpClient|PostmanRuntime|ApacheBench|Java||python-requests|Python-urllib|node-fetch)) {
    return 403;
}

# 禁止指定的 user agent 以及为空的访问
if ($http_user_agent ~ "Nimbostratus|MJ12bot|MegaIndex|YisouSpider|^$" ) {
    return 403;             
}

# 屏蔽恶意访问
if ($document_uri ~* \.(php|asp|aspx|jsp|swp|git|env|yaml|yml|sql|db|bak|ini|docx|doc|rar|tar|gz|zip|log)$) {
    return 404;
}

# 屏蔽指定关键词
if ($document_uri ~* (wordpress|phpinfo|wlwmanifest|phpMyAdmin|xmlrpc)) {
    return 404;
}

这样在 nginx.conf 中就可以直接 include 进来了。

http {
    include       mime.types;
    default_type  text/html;

    ##### limit 限流
    limit_conn_zone $binary_remote_addr zone=perIP:10m;
    limit_conn_zone $server_name zone=perServer:10m;
    limit_conn perIP 10;
    limit_conn perServer 200;
    limit_conn_status 503;

    ### 引入屏蔽规则
    include /etc/nginx/block_ips.conf;
    include /etc/nginx/block_spiders.conf;

    ### 其他配置
}

重启 nginx 即可生效。 (本机的nginx,是使用 apt 包安装的,可以直接 使用 systemctl 管理 nginx 服务)。

$ systemctl reload ngxin  # 重新加载
$ systemctl restart ngxin # 重启
$ systemctl status ngxin  # 查看状态
$ systemctl start ngxin   # 启动
$ systemctl stop ngxin    # 停止

温馨提示,重启前记得测试 配置文件是否有错误。

$ nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

确保测试通过后再重启哦。

参考链接

https://nginx.org/en/docs/

https://ssrvps.org/archives/5370