今天在看腾讯云 CDN 统计数据时,发现一个不起眼的 Referer:

image.png

按照以往的习惯总是会把一些不熟悉的网站点开看看,结果差点就没发现这个山寨货:一个和本站一模一样的网站,但是里面全是繁体字,评论系统原封不动且正常使用。

我甚至怀疑以前检查的时候我都自动忽略了。因为这次发现的镜像站是因为我发现假冒站的「友链朋友圈」没点开,循环重定向,打算找问题时发现域名不对劲。至于繁体文字我一直以为是 Butterfly 偶发故障,因为点两下简繁切换按钮就好了。

服了,我真的是要吐槽我自己了,为什么每次博客圈发生什么坏事我总是这么迟钝🤮:

  • 年初网站外链查的紧时,是蜀黍打电话过来时才把它当回事。(那时候已经看到过 @大大的蜗牛 发表过类似文章,处理措施详见 站内文章这篇文章
  • 前段时间中小博客被盗刷 CDN,我是直到钱包被抠破才意识到事情严重,甚至眼睁睁看着正在被盗刷不知道该干啥。(然而那时候已经看到过 @清羽飞扬 发表过类似文章)
  • 现在被镜像了,却不知道已经被镜像 2 个月了,然后才想起来 @杜老师 有发过类似的文章…

wcnm

既然事情已经发生,那现在我们就着手处理吧。

假冒站点特征

镜像站的地址:gatoelectric.online。从域名上看不出什么名堂,感觉是个某个做电子的企业。ICP 备案查不到什么信息(废话),Whois 展示如下:

image.png

这个来自天津的 zhang💩zhen💩yu 同时也镜像了 这位博主 的网站,可以说是个惯犯了。

Google 搜索「半方池水半方田」到是没搜索到,直接搜域名就搜到了。

image.png

image.png

通过一些博主的文章了解到,这种镜像站点的行为已经是自动化的了,目的可能是用于养域名,它们的特征有:

  • 国内网络无法访问镜像站点,返回 404
  • 主页面的语言是繁体
  • 伪装成搜索爬虫对网站进行爬取

image.png

🛡️防御措施

对网站的各个部分,我们可以采取相应的措施。

为网站添加 JavaScript 脚本

原理是检测当前站点是否为正确站点,否则:

  • 弹窗提示框,说明此站点非官方站点。或直接重定向到官方站点。
  • 站点加上水印
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
const validDomain = 'uuanqin.top';
const redirectUrl = 'https://blog.uuanqin.top';
const hostname = document.location.hostname;
const isLocalHost = (hostname === 'localhost' || hostname === '127.0.0.1');

//如果不是本地也不是子域名
if (!isLocalHost && !hostname.endsWith(validDomain)) {
createWatermark(validDomain)
const userResponse = confirm(`警告:本页面非官方页面,可能存在有害信息!建议您立即跳转 ${validDomain} 并向站长举报该镜像站!`);
if (userResponse) {
window.location.replace(redirectUrl);
}
}

// 检查是否在iframe中
if (window.top !== window.self) {
const userResponse = confirm(`当前访问于嵌套窗口,可能存在侵权行为,请在独立窗口访问 ${validDomain}。`);
if (userResponse) {
window.open(redirectUrl, '_blank');
}
}

function createWatermark(text) {
const watermarkDiv = document.createElement('div');
watermarkDiv.style.pointerEvents = 'none';
watermarkDiv.style.position = 'fixed';
watermarkDiv.style.top = '0';
watermarkDiv.style.left = '0';
watermarkDiv.style.width = '100%';
watermarkDiv.style.height = '100%';
watermarkDiv.style.zIndex = '9999';
watermarkDiv.style.opacity = '0.35';
watermarkDiv.style.background = 'transparent';
watermarkDiv.style.overflow = 'hidden';
watermarkDiv.style.display = 'flex';
watermarkDiv.style.justifyContent = 'center';
watermarkDiv.style.alignItems = 'center';
watermarkDiv.style.flexWrap = 'wrap';

const watermarkText = document.createElement('div');
watermarkText.innerText = text;
watermarkText.style.color = '#8d8d8d';
watermarkText.style.fontSize = '30px';
watermarkText.style.transform = 'rotate(-30deg)';
watermarkText.style.whiteSpace = 'nowrap';
watermarkText.style.margin = '20px';
// 文本阴影(增加可读性)
watermarkText.style.textShadow = '2px 2px 5px rgba(0, 0, 0, 0.5)'; // 添加黑色阴影,增强可读性

for (let i = 0; i < 60; i++) {
watermarkDiv.appendChild(watermarkText.cloneNode(true));
}

document.body.appendChild(watermarkDiv);
}

js 引入前记得做混淆处理:JavaScript Obfuscator Tool,勾选 unicode escape sequence 选项把中文进行转义处理。

效果立竿见影,几乎一发布就被假冒镜像站捕获到:

image.png

image.png

注意,后续更改 Js 时注意把文件名也改一改,因为镜像站的服务器有缓存。

Waline 评论系统设置安全域名

Waline 评论内容能被镜像站显示是我完全没想到的。我们可以在环境变量配置安全域名。详看:服务端环境变量 | Waline

效果为评论内容不加载,且禁止评论:

image.png

CDN 防盗链黑名单

腾讯云 CDN 访问控制开启防盗链黑名单:

image.png

注意:

  • 不用选「拒绝空 referer 访问」,这影响范围比较大,容易误伤一些正常访问,不好处理。
  • 对图床防盗链即可,意思意思。对网站不用防盗链。因为这会导致用户点击你的跳转弹窗时被官方站点拒绝。

效果:

image.png

🗡️反制措施

获得伪站的「谷歌站长」所有权

用自己的 Google 小号,在谷歌站长新加一个网站,网址就是镜像站的网址。利用镜像站原封不动爬取资源的特点,通过 HTML 验证获得镜像站的「谷歌站长」所有权。

image.png

Hexo 博客放置步骤可以是:

  1. 放到 source 文件夹下
  2. 在配置 _config.yml 中指定文件不被渲染
1
2
skip_render:
- googlefcd336221d7decf8.html # 验证文件
  1. 正常发布即可

image.png

然后,桀桀桀…(此部分需等待谷歌完成数据整理,后续博主会更新)

在 Nginx 中将假爬虫的请求转发到其他站点

首先获得谷歌爬虫的 IP 段:googlebot.json

将 IP 段转换成这种形式:

1
2
3
66.249.76.96/27 1;
66.249.77.0/27 1;
66.249.77.128/27 1;

可以找 AI 帮忙生成代码而不是直接要结果,毕竟 IP 太多了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import json

# 输入文件和输出文件的路径
input_file = 'googlebot.json'
output_file = 'converted_ipranges.txt'

# 读取 JSON 数据
with open(input_file, 'r', encoding='utf-8') as f:
data = json.load(f)

# 提取 IP 地址段
ip_ranges = data.get("prefixes", [])

# 将结果写入输出文件
with open(output_file, 'w', encoding='utf-8') as f:
for range in ip_ranges:
# 检查是否包含 ipv4Prefix 或 ipv6Prefix,并提取
if 'ipv4Prefix' in range:
f.write(f'{range["ipv4Prefix"]} 1;\n')
elif 'ipv6Prefix' in range:
f.write(f'{range["ipv6Prefix"]} 1;\n')

print(f"转换结果已保存到 {output_file}")

打开 Nginx 日志(/var/log/nginx 目录下),搜索可疑爬虫的 User Agent,如果爬虫的 IP 不在官方的列表中,就说明这个爬虫是假的。

在 Nginx 配置文件(http 块)中加入以下字段:

1
2
3
4
5
# 加载 IP 白名单文件
geo $crawler_ip {
default 0;
include /etc/nginx/conf.d/googlebot_ipranges/converted_ipranges.txt;
}

主站 location 块中加入判断:

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
server {

listen 443 ssl;
server_name blog.uuanqin.top;
access_log /var/log/nginx/host.access.log main;


location / {
root /var/www/blog.uuanqin.top;
index index.html index.htm;

set $temp "";

# 符合UA
if ($http_user_agent ~* 'compatible; Googlebot/2.1;') {
set $temp "${temp}1";
}
# 但不符合IP段时自动反代指定网页
if ($crawler_ip != 1) {
set $temp "${temp}1";
}

if ($temp ~* "11") {
proxy_pass https://www.cac.gov.cn; # 网信办
break; # 添加 break 语句,确保后续的处理被中断
}
}

# 此处省略其他配置
}

配置更改后记得重启 Nginx:

1
nginx -s reload
写 Nginx 配置文件遇到的各种坑

特么的,这些东西花了我几个小时排查,GPT-4o 也不靠谱。可能是我的 Nginx 版本原因把,写个简单的「与」条件判断够复杂的。

  • geomap 必须在 server 块外面
  • if 条件语句写在 location 里面
  • if 条件语句不能嵌套
  • if 条件语句不能使用「与」「或」判断
  • if 和括号 ( 中间必须有空格
  • proxy_pass 接的网址最后不能是 /
  • 奇怪的正则表达式

我们可以通过修改输出日志的格式完成 Nginx 的调试:

1
2
3
4
5
6
7
log_format alogformat '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for -- is_crawler_ip: $crawler_ip | fakeBotWillBe11: $temp "';

server{
access_log /var/log/nginx/host.access.log alogformat;
}

通过日志查看指定的 IP 是否被拦截。

然而并没有什么用,因为伪站不只使用假爬虫伪装,它可能还使用 Cloudflare 的代理结点进行爬取。

向各种服务商举报滥用

我们可以向 Google、Bing、Cloudflare、Aliyun、域名商以 DMCA 为理由举报滥用行为。

不懂有没有用哈。

本文参考

镜像站相关:

Nginx 相关: