用 20 行 JS 让镜像站流量归零
起因
UpXuu 的博客被人用 Cloudflare Worker 全站镜像——所有文章、图片、页面内容实时同步到另一个域名,未获任何授权。攻击者的套路很简单:wget --mirror 一把梭下载全站,往 Nginx 一丢,改个域名,流量就进了别人口袋。
这种事在技术博客圈不罕见。检查了一下 nixegus.com——没有防护。
那就加上。
核心思路
镜像站的本质矛盾在于:攻击者下载的是静态文件,真实访客打开的是浏览器。 浏览器执行 JavaScript,wget/curl/httrack 不执行。
利用这个差异:在页面嵌入一段 JS,检查当前域名是否匹配,不匹配就跳回真站。
代码
function getTargetHost() {
const XOR_KEY = "97vaMcLo";
const XOR_CIPHER = atob("TkABTyMKNApeQgVPLgwh");
let host = '';
for (let i = 0; i < XOR_CIPHER.length; i++) {
host += String.fromCharCode(XOR_CIPHER.charCodeAt(i) ^ XOR_KEY.charCodeAt(i % XOR_KEY.length));
}
return host;
}
(function () {
const targetHost = getTargetHost().trim();
if (window.location.host === targetHost) return;
const targetUrl = window.location.protocol + "//" + targetHost + window.location.pathname + window.location.search + window.location.hash;
window.location.replace(targetUrl);
})();
几个设计选择:
- XOR 加密域名:不是防破解,是防止攻击者对 HTML 全文搜索域名一键替换。密钥和密文分开存,Base64 再包一层,增加自动化处理的成本。真想破解的话看一眼 JS 就明白了,但批量攻击者不会为这一个站动手。
location.replace():不用location.href,访客点后退回不到镜像站。location.host而非hostname:host 包含端口号,确保即使通过带端口的方式访问,也能正确判断域名归属。- 沿用当前页面的协议:镜像站用什么协议提供服务,目标 URL 就用什么协议——通常镜像站和真站的协议一致,不需要硬编码。
它能挡住什么
防的不是下载——静态文件本身就是公开的,wget、httrack、curl 都能完整拉走。防的是真实访客通过镜像域名访问页面时,自动跳回真站。
| 场景 | 结果 |
|---|---|
| 直接访问 nixegus.com | 正常,host 匹配,脚本跳过 |
| 浏览器访问镜像站 | 自动跳回 nixegus.com |
| hosts 劫持域名到恶意 IP | 无效——host 字面匹配,劫持不了 |
| 访客关闭 JavaScript | 无效,JS 不执行 |
| 搜索引擎爬虫 | 正常收录,爬虫不执行 JS |
hosts 劫持和禁用 JS 这两种情况无法防御,但它们不影响这个方案的实际效果——批量镜像攻击者的目标是 "偷流量",不是 "攻克特定站点"。只要还有无防御的站可以镜像,他们不会为任何一个站多花一分钟。
成本效益
① wget --mirror 下载全站 ← 成本 0
② 丢到 Nginx 上 ← 成本 0
③ 发现 JS 跳转 → 流量为零 ← 收入 0
④ 选择:花时间去掉 JS / 换一个站
⑤ → 换一个站 ← 成本 0,收入正常
花 10 分钟写后处理脚本就能绕过,但批量攻击者面对的不是密码学难题,是一道经济题。无防御的站要多少有多少,他们不会选择动手的。
总结
这个方案不是 "无法绕过",而是 "不值得绕过"。20 行 JS,零服务器开销,零依赖,对正常访客完全无感——当绕过成本远高于换一个目标时,方案就站住了。
评论