屏蔽了一些爬虫

本来读了研也确实没有本科那么强的折腾精神了,服务器除了续费和升级软件也没怎么动过。按理说这么小的网站 Googlebot 都基本不访问才对,但偶然检查一下还是发现了一些异样。以下分享两则最近发现的乱爬案例。

阿里云阴兵过境

第一关是不明新加坡阿里云爬虫。

位于香港的服务器上一直运行着一个 Gitea 做自己 GitHub 的镜像,由于某些原因没有挂 Cloudflare,我那点垃圾代码按理说也没人看吧。诶恰恰相反,其实早在很久之前就有相当数量的爬虫在爬,只是比较节制,不会爬个没完。直到前段时间登上腾讯云,发现一个月竟然差不多用完了 1TB 的流量,并非互联网上一个偏远角落的正常访问量。以 7 天为尺度观察统计图表,好家伙直接把我 CPU 全跑满了呀。

腾讯云的统计
腾讯云的统计

不过话说回来幸亏是 CPU-bound 的 Gitea,CPU 打满了,30M 的带宽还没打满,不然下场估计是先被腾讯断网,现在跑得慢但起码还能访问。导出 Gitea 的日志一看(共 49 万行),大部分是我本科边抄边写的操作系统实验,算是别人的代码,不是垃圾代码:

log
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/rss/commit/5204cd45e8a0f09f7a27ddad803bf28d56474dba/labcodes_answer/lab3_result/tools/sign.c for 47.79.214.90:0, 404 Not Found in 0.2ms @ <autogenerated>:1(WebNotFound)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/commits/commit/5204cd45e8a0f09f7a27ddad803bf28d56474dba/labcodes_answer/lab3_result/tools/kernel.ld for 47.79.199.163:0, 200 OK in 365.3ms @ repo/commit.go:44(repo.RefCommits)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/src/commit/a29ae0982ac021cc2abeb75004af69b1b0d29f53/labcodes_answer/lab8_result/libs/printfmt.c for 47.79.215.248:0, 200 OK in 518.0ms @ repo/view_home.go:332(repo.Home)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/src/commit/a29ae0982ac021cc2abeb75004af69b1b0d29f53/labcodes_answer/lab1_result/tools/lab1init for 47.79.197.254:0, 200 OK in 481.5ms @ repo/view_home.go:332(repo.Home)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/rss/commit/5204cd45e8a0f09f7a27ddad803bf28d56474dba/labcodes_answer/lab2_result/libs/stdarg.h for 47.79.193.156:0, 404 Not Found in 0.5ms @ <autogenerated>:1(WebNotFound)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/commits/commit/0a7d3faa0b7804e45252a70e583ede3459b16949/labcodes/lab7/tools for 47.79.213.176:0, 200 OK in 223.1ms @ repo/commit.go:44(repo.RefCommits)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/src/commit/a29ae0982ac021cc2abeb75004af69b1b0d29f53/labcodes_answer/lab8_result/libs/string.h for 47.79.199.252:0, 200 OK in 568.3ms @ repo/view_home.go:332(repo.Home)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/raw/commit/5204cd45e8a0f09f7a27ddad803bf28d56474dba/labcodes_answer/lab2_result/libs/error.h for 47.79.215.8:0, 200 OK in 201.5ms @ repo/download.go:111(repo.SingleDownload)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/src/commit/0a7d3faa0b7804e45252a70e583ede3459b16949/labcodes_answer/lab2_result/kern/debug for 47.79.204.14:0, 200 OK in 351.2ms @ repo/view_home.go:332(repo.Home)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/src/commit/5204cd45e8a0f09f7a27ddad803bf28d56474dba/labcodes_answer/lab2_result/kern/libs/readline.c for 47.79.213.33:0, 200 OK in 519.4ms @ repo/view_home.go:332(repo.Home)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/raw/commit/5204cd45e8a0f09f7a27ddad803bf28d56474dba/labcodes_answer/lab3_result/tools/kernel.ld for 47.79.194.9:0, 200 OK in 121.7ms @ repo/download.go:111(repo.SingleDownload)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/src/commit/a29ae0982ac021cc2abeb75004af69b1b0d29f53/labcodes_answer/lab8_result/libs/stdlib.h for 47.79.192.216:0, 200 OK in 272.8ms @ repo/view_home.go:332(repo.Home)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/src/commit/a29ae0982ac021cc2abeb75004af69b1b0d29f53/labcodes_answer/lab8_result/libs/dirent.h for 47.79.205.12:0, 200 OK in 283.5ms @ repo/view_home.go:332(repo.Home)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/commits/commit/a29ae0982ac021cc2abeb75004af69b1b0d29f53/labcodes_answer/lab8_result/libs for 47.79.212.251:0, 200 OK in 163.6ms @ repo/commit.go:44(repo.RefCommits)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/blame/commit/d41b9e7f4fce933f5ce58c9c42e2f0bd5c7f6fc4/labcodes/lab7/Makefile for 47.79.213.33:0, 200 OK in 521.9ms @ repo/blame.go:42(repo.RefBlame)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/src/commit/0a7d3faa0b7804e45252a70e583ede3459b16949/labcodes/lab7/tools/kernel.ld for 47.79.194.123:0, 200 OK in 392.8ms @ repo/view_home.go:332(repo.Home)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/src/commit/5204cd45e8a0f09f7a27ddad803bf28d56474dba/labcodes_answer/lab3_result/tools for 47.79.192.112:0, 200 OK in 138.9ms @ repo/view_home.go:332(repo.Home)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/commits/commit/5204cd45e8a0f09f7a27ddad803bf28d56474dba/labcodes_answer/lab3_result/tools/sign.c for 47.79.205.59:0, 200 OK in 105.0ms @ repo/commit.go:44(repo.RefCommits)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/src/commit/a29ae0982ac021cc2abeb75004af69b1b0d29f53/labcodes_answer/lab8_result/libs/defs.h for 47.79.192.12:0, 200 OK in 154.0ms @ repo/view_home.go:332(repo.Home)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/src/commit/a29ae0982ac021cc2abeb75004af69b1b0d29f53/labcodes_answer/lab8_result/libs/x86.h for 47.79.192.12:0, 200 OK in 345.3ms @ repo/view_home.go:332(repo.Home)
Aug 05 08:57:04 VM-8-17-ubuntu gitea[4160705]: 2025/08/05 08:57:04 ...eb/routing/logger.go:102:func1() [I] router: completed GET /cyp0633/ucore-lab/blame/commit/5204cd45e8a0f09f7a27ddad803bf28d56474dba/labcodes_answer/lab2_result/libs/defs.h for 47.79.198.73:0, 200 OK in 164.4ms @ repo/blame.go:42(repo.RefBlame)

vibe 了一个分析脚本,发现 28 分钟的记录时间内处理了 56182 个请求,平均每分钟高达 2000 多个;请求来自于 750 个不同的 IP,全都来自于 47.79.0.0/16 这个阿里云新加坡的 IP 段,这样分摊到每个 IP 上,半个小时不到 200 次请求,看起来非常的人畜无害。这使得手写 IP 封禁或者速率限制变得不切实际。为了保险起见,该日先把 Gitea 关掉了。

为了进一步确认这些 IP 的身份,将 Gitea 暂时关闭数天后,还导出了 Caddy 的日志。

log
Aug 19 21:00:32 VM-8-17-ubuntu caddy[5430]: {"level":"error","ts":1755608432.2537313,"logger":"http.log.error","msg":"dial tcp :3001: connect: connection refused","request":{"remote_ip":"47.79.201.138","remote_port":"56801","client_ip":"47.79.201.138","proto":"HTTP/1.1","method":"GET","host":"git.cyp0633.icu","uri":"/cyp0633/stack-mod/raw/commit/b66679d36acc0443078dafdfc5452880f376e015/layouts/partials/footer/script.html","headers":{"Cache-Control":["no-cache"],"Accept":["*/*"],"Sec-Ch-Ua-Platform":["\"macOS\""],"Accept-Language":["en,en-US"],"Sec-Fetch-Mode":["navigate"],"Sec-Fetch-Site":["none"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\""],"Connection":["keep-alive"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Upgrade-Insecure-Requests":["1"],"Sec-Fetch-User":["?1"],"Priority":["u=0, i"],"Sec-Fetch-Dest":["document"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124"],"Accept-Encoding":["gzip, deflate"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"http/1.1","server_name":"git.cyp0633.icu"}},"duration":0.000190728,"status":502,"err_id":"00uu9mrsp","err_trace":"reverseproxy.statusError (reverseproxy.go:1373)"}
Aug 19 21:00:32 VM-8-17-ubuntu caddy[5430]: {"level":"error","ts":1755608432.260209,"logger":"http.log.error","msg":"dial tcp :3001: connect: connection refused","request":{"remote_ip":"47.79.212.84","remote_port":"39054","client_ip":"47.79.212.84","proto":"HTTP/1.1","method":"GET","host":"git.cyp0633.icu","uri":"/cyp0633/stack-mod/src/commit/25581cc44637909a75aacdf33f896192f0569689/layouts/partials/head/opengraph/include.html","headers":{"Accept-Language":["en,en-US"],"Connection":["keep-alive"],"Priority":["u=0, i"],"Sec-Fetch-Mode":["navigate"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124"],"Accept-Encoding":["gzip, deflate"],"Accept":["*/*"],"Sec-Ch-Ua-Platform":["\"macOS\""],"Sec-Fetch-Dest":["document"],"Pragma":["no-cache"],"Upgrade-Insecure-Requests":["1"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\""],"Sec-Fetch-Site":["none"],"Sec-Fetch-User":["?1"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"http/1.1","server_name":"git.cyp0633.icu"}},"duration":0.000227457,"status":502,"err_id":"aa84vne2s","err_trace":"reverseproxy.statusError (reverseproxy.go:1373)"}
Aug 19 21:00:32 VM-8-17-ubuntu caddy[5430]: {"level":"error","ts":1755608432.2617188,"logger":"http.log.error","msg":"dial tcp :3001: connect: connection refused","request":{"remote_ip":"47.79.206.1","remote_port":"30313","client_ip":"47.79.206.1","proto":"HTTP/1.1","method":"GET","host":"git.cyp0633.icu","uri":"/cyp0633/stack-mod/blame/commit/25a72940b8184b41de36fec9a3fcdd6387e006b0/layouts/partials/sidebar/left.html","headers":{"Sec-Fetch-Dest":["document"],"Upgrade-Insecure-Requests":["1"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Accept-Encoding":["gzip, deflate"],"Pragma":["no-cache"],"Connection":["keep-alive"],"Sec-Fetch-Mode":["navigate"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124"],"Sec-Fetch-Site":["none"],"Sec-Fetch-User":["?1"],"Priority":["u=0, i"],"Sec-Ch-Ua-Platform":["\"macOS\""],"Accept-Language":["en,en-US"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\""],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"http/1.1","server_name":"git.cyp0633.icu"}},"duration":0.000177793,"status":502,"err_id":"7jqfa7yiq","err_trace":"reverseproxy.statusError (reverseproxy.go:1373)"}
Aug 19 21:00:32 VM-8-17-ubuntu caddy[5430]: {"level":"error","ts":1755608432.2736785,"logger":"http.log.error","msg":"dial tcp :3001: connect: connection refused","request":{"remote_ip":"47.79.198.78","remote_port":"25315","client_ip":"47.79.198.78","proto":"HTTP/1.1","method":"GET","host":"git.cyp0633.icu","uri":"/cyp0633/stack-mod/raw/commit/2fd3bde9a40790bf98893b0c5062a2f4c0ac9d8f/layouts/partials/article-list/compact.html","headers":{"Sec-Ch-Ua-Platform":["\"macOS\""],"Connection":["keep-alive"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Fetch-Site":["none"],"Accept":["*/*"],"Sec-Ch-Ua-Mobile":["?0"],"Accept-Language":["en,en-US"],"Sec-Fetch-Dest":["document"],"Upgrade-Insecure-Requests":["1"],"Sec-Fetch-Mode":["navigate"],"Sec-Fetch-User":["?1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124"],"Accept-Encoding":["gzip, deflate"],"Priority":["u=0, i"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\""]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"http/1.1","server_name":"git.cyp0633.icu"}},"duration":0.000182872,"status":502,"err_id":"89gyde5bz","err_trace":"reverseproxy.statusError (reverseproxy.go:1373)"}
Aug 19 21:00:32 VM-8-17-ubuntu caddy[5430]: {"level":"error","ts":1755608432.2741818,"logger":"http.log.error","msg":"dial tcp :3001: connect: connection refused","request":{"remote_ip":"47.79.215.201","remote_port":"12618","client_ip":"47.79.215.201","proto":"HTTP/1.1","method":"GET","host":"git.cyp0633.icu","uri":"/cyp0633/stack-mod/commits/commit/25a72940b8184b41de36fec9a3fcdd6387e006b0/layouts/partials/sidebar/left.html","headers":{"Sec-Ch-Ua-Mobile":["?0"],"Accept":["*/*"],"Accept-Language":["en,en-US"],"Priority":["u=0, i"],"Sec-Fetch-Site":["none"],"Connection":["keep-alive"],"Upgrade-Insecure-Requests":["1"],"Sec-Fetch-Dest":["document"],"Accept-Encoding":["gzip, deflate"],"Sec-Ch-Ua-Platform":["\"macOS\""],"Sec-Fetch-Mode":["navigate"],"Sec-Fetch-User":["?1"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\""],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"http/1.1","server_name":"git.cyp0633.icu"}},"duration":0.000194264,"status":502,"err_id":"mzpuq5vau","err_trace":"reverseproxy.statusError (reverseproxy.go:1373)"}
Aug 19 21:00:32 VM-8-17-ubuntu caddy[5430]: {"level":"error","ts":1755608432.2894285,"logger":"http.log.error","msg":"dial tcp :3001: connect: connection refused","request":{"remote_ip":"47.79.195.97","remote_port":"5843","client_ip":"47.79.195.97","proto":"HTTP/1.1","method":"GET","host":"git.cyp0633.icu","uri":"/cyp0633/stack-mod/commits/commit/2fd3bde9a40790bf98893b0c5062a2f4c0ac9d8f/layouts/partials/article-list/tile.html","headers":{"Accept":["*/*"],"Cache-Control":["no-cache"],"Sec-Fetch-Mode":["navigate"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["none"],"Upgrade-Insecure-Requests":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\""],"Pragma":["no-cache"],"Priority":["u=0, i"],"Accept-Encoding":["gzip, deflate"],"Sec-Ch-Ua-Platform":["\"macOS\""],"Sec-Fetch-Dest":["document"],"Sec-Fetch-User":["?1"],"Connection":["keep-alive"],"Accept-Language":["en,en-US"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"http/1.1","server_name":"git.cyp0633.icu"}},"duration":0.00023476,"status":502,"err_id":"v3azrf597","err_trace":"reverseproxy.statusError (reverseproxy.go:1373)"}
Aug 19 21:00:32 VM-8-17-ubuntu caddy[5430]: {"level":"error","ts":1755608432.2902029,"logger":"http.log.error","msg":"dial tcp :3001: connect: connection refused","request":{"remote_ip":"47.79.193.3","remote_port":"51386","client_ip":"47.79.193.3","proto":"HTTP/1.1","method":"GET","host":"git.cyp0633.icu","uri":"/cyp0633/stack-mod/blame/commit/d04b3a8771386c9988be00adda53f0bf542cf269/layouts/partials/helper/image.html","headers":{"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Accept-Encoding":["gzip, deflate"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\""],"Upgrade-Insecure-Requests":["1"],"Accept-Language":["en,en-US"],"Sec-Fetch-Mode":["navigate"],"Sec-Fetch-User":["?1"],"Sec-Ch-Ua-Platform":["\"macOS\""],"Connection":["keep-alive"],"Sec-Fetch-Dest":["document"],"Sec-Fetch-Site":["none"],"Accept":["*/*"],"Priority":["u=0, i"],"Pragma":["no-cache"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"http/1.1","server_name":"git.cyp0633.icu"}},"duration":0.000307345,"status":502,"err_id":"6gv9knvpm","err_trace":"reverseproxy.statusError (reverseproxy.go:1373)"}
Aug 19 21:00:32 VM-8-17-ubuntu caddy[5430]: {"level":"error","ts":1755608432.2950988,"logger":"http.log.error","msg":"dial tcp :3001: connect: connection refused","request":{"remote_ip":"47.79.193.80","remote_port":"24245","client_ip":"47.79.193.80","proto":"HTTP/1.1","method":"GET","host":"git.cyp0633.icu","uri":"/cyp0633/stack-mod/commits/commit/c6661196ad92dfa72513582c40f75a6a5f514851/layouts/partials?page=1","headers":{"Sec-Ch-Ua-Mobile":["?0"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124"],"Sec-Fetch-User":["?1"],"Sec-Ch-Ua-Platform":["\"macOS\""],"Accept":["*/*"],"Upgrade-Insecure-Requests":["1"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\""],"Pragma":["no-cache"],"Sec-Fetch-Dest":["document"],"Accept-Encoding":["gzip, deflate"],"Cache-Control":["no-cache"],"Accept-Language":["en,en-US"],"Priority":["u=0, i"],"Sec-Fetch-Mode":["navigate"],"Connection":["keep-alive"],"Sec-Fetch-Site":["none"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"http/1.1","server_name":"git.cyp0633.icu"}},"duration":0.000194966,"status":502,"err_id":"ju8k688tg","err_trace":"reverseproxy.statusError (reverseproxy.go:1373)"}
Aug 19 21:00:32 VM-8-17-ubuntu caddy[5430]: {"level":"error","ts":1755608432.3023942,"logger":"http.log.error","msg":"dial tcp :3001: connect: connection refused","request":{"remote_ip":"47.79.205.248","remote_port":"12381","client_ip":"47.79.205.248","proto":"HTTP/1.1","method":"GET","host":"git.cyp0633.icu","uri":"/cyp0633/stack-mod/rss/commit/0af9d23e4989ed7ada10e6990802ccd9a28d8797/layouts/partials/sidebar/right.html","headers":{"Sec-Ch-Ua-Platform":["\"macOS\""],"Sec-Ch-Ua":["\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\""],"Sec-Fetch-Mode":["navigate"],"Upgrade-Insecure-Requests":["1"],"Pragma":["no-cache"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["document"],"Sec-Fetch-User":["?1"],"Accept-Language":["en,en-US"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124"],"Accept-Encoding":["gzip, deflate"],"Accept":["*/*"],"Priority":["u=0, i"],"Connection":["keep-alive"],"Sec-Fetch-Site":["none"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"http/1.1","server_name":"git.cyp0633.icu"}},"duration":0.000187251,"status":502,"err_id":"4m19ms28b","err_trace":"reverseproxy.statusError (reverseproxy.go:1373)"}
Aug 19 21:00:32 VM-8-17-ubuntu caddy[5430]: {"level":"error","ts":1755608432.3110833,"logger":"http.log.error","msg":"dial tcp :3001: connect: connection refused","request":{"remote_ip":"47.79.212.84","remote_port":"57044","client_ip":"47.79.212.84","proto":"HTTP/1.1","method":"GET","host":"git.cyp0633.icu","uri":"/cyp0633/stack-mod/commits/commit/2fd3bde9a40790bf98893b0c5062a2f4c0ac9d8f/layouts/partials/article-list/compact.html","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124"],"Sec-Fetch-Site":["none"],"Priority":["u=0, i"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\""],"Sec-Ch-Ua-Platform":["\"macOS\""],"Upgrade-Insecure-Requests":["1"],"Sec-Ch-Ua-Mobile":["?0"],"Connection":["keep-alive"],"Sec-Fetch-Mode":["navigate"],"Sec-Fetch-User":["?1"],"Accept":["*/*"],"Accept-Language":["en,en-US"],"Accept-Encoding":["gzip, deflate"],"Sec-Fetch-Dest":["document"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"http/1.1","server_name":"git.cyp0633.icu"}},"duration":0.000179277,"status":502,"err_id":"hk01fb751","err_trace":"reverseproxy.statusError (reverseproxy.go:1373)"}
Aug 19 21:00:32 VM-8-17-ubuntu caddy[5430]: {"level":"error","ts":1755608432.31186,"logger":"http.log.error","msg":"dial tcp :3001: connect: connection refused","request":{"remote_ip":"47.79.205.70","remote_port":"51797","client_ip":"47.79.205.70","proto":"HTTP/1.1","method":"GET","host":"git.cyp0633.icu","uri":"/cyp0633/stack-mod/src/commit/d04b3a8771386c9988be00adda53f0bf542cf269/layouts/partials/helper/icon.html","headers":{"Accept":["*/*"],"Accept-Language":["en,en-US"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124"],"Pragma":["no-cache"],"Sec-Fetch-Site":["none"],"Sec-Ch-Ua-Mobile":["?0"],"Accept-Encoding":["gzip, deflate"],"Priority":["u=0, i"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\""],"Sec-Fetch-Dest":["document"],"Cache-Control":["no-cache"],"Connection":["keep-alive"],"Upgrade-Insecure-Requests":["1"],"Sec-Ch-Ua-Platform":["\"macOS\""],"Sec-Fetch-User":["?1"],"Sec-Fetch-Mode":["navigate"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"http/1.1","server_name":"git.cyp0633.icu"}},"duration":0.00037888,"status":502,"err_id":"7dz5tnw6r","err_trace":"reverseproxy.statusError (reverseproxy.go:1373)"}
Aug 19 21:00:32 VM-8-17-ubuntu caddy[5430]: {"level":"error","ts":1755608432.3274698,"logger":"http.log.error","msg":"dial tcp :3001: connect: connection refused","request":{"remote_ip":"47.79.200.118","remote_port":"13643","client_ip":"47.79.200.118","proto":"HTTP/1.1","method":"GET","host":"git.cyp0633.icu","uri":"/cyp0633/stack-mod/rss/commit/0af9d23e4989ed7ada10e6990802ccd9a28d8797/layouts/partials/data/description.html","headers":{"Priority":["u=0, i"],"Sec-Ch-Ua-Mobile":["?0"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124"],"Sec-Fetch-Mode":["navigate"],"Sec-Ch-Ua-Platform":["\"macOS\""],"Connection":["keep-alive"],"Cache-Control":["no-cache"],"Sec-Fetch-User":["?1"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\""],"Upgrade-Insecure-Requests":["1"],"Sec-Fetch-Dest":["document"],"Accept-Encoding":["gzip, deflate"],"Accept":["*/*"],"Accept-Language":["en,en-US"],"Sec-Fetch-Site":["none"],"Pragma":["no-cache"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"http/1.1","server_name":"git.cyp0633.icu"}},"duration":0.000217268,"status":502,"err_id":"ht5drxez1","err_trace":"reverseproxy.statusError (reverseproxy.go:1373)"}
Aug 19 21:00:32 VM-8-17-ubuntu caddy[5430]: {"level":"error","ts":1755608432.3605616,"logger":"http.log.error","msg":"dial tcp :3001: connect: connection refused","request":{"remote_ip":"47.79.215.215","remote_port":"44762","client_ip":"47.79.215.215","proto":"HTTP/1.1","method":"GET","host":"git.cyp0633.icu","uri":"/cyp0633/stack-mod/src/commit/25581cc44637909a75aacdf33f896192f0569689/layouts/partials/head/opengraph/provider","headers":{"Sec-Fetch-Site":["none"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\""],"Cache-Control":["no-cache"],"Priority":["u=0, i"],"Sec-Fetch-User":["?1"],"Pragma":["no-cache"],"Accept-Language":["en,en-US"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["document"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124"],"Connection":["keep-alive"],"Upgrade-Insecure-Requests":["1"],"Accept-Encoding":["gzip, deflate"],"Sec-Ch-Ua-Platform":["\"macOS\""],"Accept":["*/*"],"Sec-Fetch-Mode":["navigate"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"http/1.1","server_name":"git.cyp0633.icu"}},"duration":0.000191609,"status":502,"err_id":"fhuzf2976","err_trace":"reverseproxy.statusError (reverseproxy.go:1373)"}
Aug 19 21:00:32 VM-8-17-ubuntu caddy[5430]: {"level":"error","ts":1755608432.3730135,"logger":"http.log.error","msg":"dial tcp :3001: connect: connection refused","request":{"remote_ip":"47.79.215.163","remote_port":"20753","client_ip":"47.79.215.163","proto":"HTTP/1.1","method":"GET","host":"git.cyp0633.icu","uri":"/cyp0633/stack-mod/raw/commit/0af9d23e4989ed7ada10e6990802ccd9a28d8797/layouts/partials/data/description.html","headers":{"Sec-Ch-Ua":["\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\""],"Pragma":["no-cache"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Platform":["\"macOS\""],"Sec-Fetch-Mode":["navigate"],"Connection":["keep-alive"],"Accept-Language":["en,en-US"],"Accept-Encoding":["gzip, deflate"],"Upgrade-Insecure-Requests":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124"],"Accept":["*/*"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["none"],"Sec-Fetch-Dest":["document"],"Sec-Fetch-User":["?1"],"Priority":["u=0, i"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"http/1.1","server_name":"git.cyp0633.icu"}},"duration":0.000226774,"status":502,"err_id":"kprf6bcji","err_trace":"reverseproxy.statusError (reverseproxy.go:1373)"}
Aug 19 21:00:32 VM-8-17-ubuntu caddy[5430]: {"level":"error","ts":1755608432.4402184,"logger":"http.log.error","msg":"dial tcp :3001: connect: connection refused","request":{"remote_ip":"47.79.195.231","remote_port":"49027","client_ip":"47.79.195.231","proto":"HTTP/1.1","method":"GET","host":"git.cyp0633.icu","uri":"/cyp0633/stack-mod/raw/commit/25581cc44637909a75aacdf33f896192f0569689/layouts/partials/head/style.html","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124"],"Sec-Ch-Ua-Mobile":["?0"],"Pragma":["no-cache"],"Sec-Fetch-User":["?1"],"Cache-Control":["no-cache"],"Connection":["keep-alive"],"Accept":["*/*"],"Sec-Fetch-Mode":["navigate"],"Upgrade-Insecure-Requests":["1"],"Accept-Language":["en,en-US"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\""],"Sec-Fetch-Dest":["document"],"Sec-Fetch-Site":["none"],"Accept-Encoding":["gzip, deflate"],"Priority":["u=0, i"],"Sec-Ch-Ua-Platform":["\"macOS\""]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"http/1.1","server_name":"git.cyp0633.icu"}},"duration":0.000254457,"status":502,"err_id":"yp9xbpai2","err_trace":"reverseproxy.statusError (reverseproxy.go:1373)"}
Aug 19 21:00:32 VM-8-17-ubuntu caddy[5430]: {"level":"error","ts":1755608432.4595432,"logger":"http.log.error","msg":"dial tcp :3001: connect: connection refused","request":{"remote_ip":"47.79.200.216","remote_port":"3491","client_ip":"47.79.200.216","proto":"HTTP/1.1","method":"GET","host":"git.cyp0633.icu","uri":"/cyp0633/stack-mod/raw/commit/d04b3a8771386c9988be00adda53f0bf542cf269/layouts/partials/helper/icon.html","headers":{"Connection":["keep-alive"],"Accept-Encoding":["gzip, deflate"],"Sec-Fetch-Dest":["document"],"Cache-Control":["no-cache"],"Accept-Language":["en,en-US"],"Accept":["*/*"],"Sec-Fetch-Site":["none"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"macOS\""],"Pragma":["no-cache"],"Sec-Fetch-User":["?1"],"Sec-Fetch-Mode":["navigate"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124"],"Priority":["u=0, i"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\""],"Upgrade-Insecure-Requests":["1"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"http/1.1","server_name":"git.cyp0633.icu"}},"duration":0.000294471,"status":502,"err_id":"txnhq2xhy","err_trace":"reverseproxy.statusError (reverseproxy.go:1373)"}
Aug 19 21:00:32 VM-8-17-ubuntu caddy[5430]: {"level":"error","ts":1755608432.4613986,"logger":"http.log.error","msg":"dial tcp :3001: connect: connection refused","request":{"remote_ip":"47.79.200.119","remote_port":"54239","client_ip":"47.79.200.119","proto":"HTTP/1.1","method":"GET","host":"git.cyp0633.icu","uri":"/cyp0633/stack-mod/src/commit/25a72940b8184b41de36fec9a3fcdd6387e006b0/layouts/partials/sidebar/left.html","headers":{"Sec-Ch-Ua-Mobile":["?0"],"Priority":["u=0, i"],"Upgrade-Insecure-Requests":["1"],"Cache-Control":["no-cache"],"Connection":["keep-alive"],"Pragma":["no-cache"],"Accept-Language":["en,en-US"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\""],"Accept-Encoding":["gzip, deflate"],"Sec-Ch-Ua-Platform":["\"macOS\""],"Sec-Fetch-Dest":["document"],"Accept":["*/*"],"Sec-Fetch-Mode":["navigate"],"Sec-Fetch-Site":["none"],"Sec-Fetch-User":["?1"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"http/1.1","server_name":"git.cyp0633.icu"}},"duration":0.000191127,"status":502,"err_id":"a7uvn7yxk","err_trace":"reverseproxy.statusError (reverseproxy.go:1373)"}

没想到即使遭遇了数天的 HTTP 502,这些 IP 还是不死心,在坚持不懈地进行访问。一模一样的 IP 段,一模一样的访问模式,一模一样的假 User-Agent。在意识到还是不如套个 Cloudflare 来得方便后,我便打开了代理开关,把这些 IP 全都上报到了 AbuseIPDB。这个阿里云用户似乎没有完全罢休,在切换到 Cloudflare 后仍然锲而不舍地爬了好一会,造成了 Cloudflare 访问统计上的一座高峰;之后还使用阿里云香港,隶属于 AS45102 的 IP 继续尝试访问,不过都被 Cloudflare 挡下来了。

Cloudflare 访问统计
Cloudflare 访问统计

刷爆对象存储,但是细水长流

前段时间阿里云给我发了条短信:

【阿里云】尊敬的cyp*33,截至2025-08-21 18:02:57,您的剩余可用额度为1.24元。根据您以往消费情况预测,剩余可用额度可能不足以支撑您未来7天的消费,为了避免欠费导致产品停止服务,请您及时充值。您可以登录阿里云官网查询账户可用额度信息和延停权益。

当时没太在意,以为是正常的余额消耗,因为虽然 OSS 一个月花不了几毛钱,但阿里云上还有大模型推理,可能最近用模型太多了。然而过了四天,就用掉了 0.71 元,消耗速度有些异常:

【阿里云】尊敬的cyp*33,截至2025-08-25 15:02:10,您的剩余可用额度为0.53元。根据您以往消费情况预测,剩余可用额度可能不足以支撑您未来3天的消费。为了避免欠费导致产品停止服务,请您及时充值。您可以登录阿里云官网查询账户可用额度信息和延停权益。

进阿里云一看,才发现是 OSS 在花钱。为了节省 Netlify 的空间,提高国内的访问速度,Netlify 上仅有博客的核心静态资源,而图片等大文件托管在内地的一个公共读阿里云 OSS 上。虽然开启了 Referer 白名单防盗链,但鉴于 Referer 投可以伪造,所以还是不保险。相比于之前的消耗速度,一天两毛钱已经算是警告了:如果再不处理,恐怕真的要欠费了。

依旧导出了一些 OSS 的日志:

csv
__source__,__time__,__topic__,acc_access_region,access_id,archive_direct_read_size,bucket,bucket_location,bucket_storage_type,cdn_in,cdn_out,client_ip,content_length_in,content_length_out,delta_data_size,ec,end_time,error_code,extend_information,get_request,host,http_method,http_status,http_type,intranet_in,intranet_out,logging_flag,metering_datasize,metering_datasize_ca,metering_datasize_zrs,metering_datasize_zrsii,network_in,network_out,object,object_size,operation,owner_id,process_img,process_img_size,put_request,referer,region,request_id,request_length,request_uri,requester_id,response_body_length,response_time,restore_priority,server_cost_time,sign_type,start_time,storage,storage_type,sync_in,sync_out,sync_request,target_storage_class,time,user_agent,user_defined_log_fields,vpc_addr,vpc_id
log_dispatch,1756752249,oss_access_log,-,-,-,cyp0633-blogasset,oss-cn-qingdao-h,standard,,,46.62.169.27,-,185189,-,-,,-,-,,cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,GET,200,https,,,true,,,,,,,need-for-speed-unbound%2FNeed%20for%20Speed%E2%84%A2%20Unbound%202023-01-22%2010-57-14.mp4_20230122_151607.850.avif,185189,GetObject,1385315732671155,,,,https://cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,,68B5E979E8569830337BD3AC,750,/need-for-speed-unbound/Need%20for%20Speed%E2%84%A2%20Unbound%202023-01-22%2010-57-14.mp4_20230122_151607.850.avif HTTP/1.1,-,185189,24,-,22,NotSign,,,,,,-,-,02/Sep/2025:02:44:09,"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",-,172563320,0
log_dispatch,1756752249,oss_access_log,-,-,-,cyp0633-blogasset,oss-cn-qingdao-h,standard,,,46.62.169.27,-,76636,-,-,,-,-,,cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,GET,200,https,,,true,,,,,,,nuc-homelab%2Fdiskperf.avif,76636,GetObject,1385315732671155,,,,https://cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,,68B5E97978A51C363838F585,662,/nuc-homelab/diskperf.avif HTTP/1.1,-,76636,41,-,41,NotSign,,,,,,-,-,02/Sep/2025:02:44:09,"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",-,172563320,0
log_dispatch,1756752249,oss_access_log,-,-,-,cyp0633-blogasset,oss-cn-qingdao-h,standard,,,46.62.169.27,-,233860,-,-,,-,-,,cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,GET,200,https,,,true,,,,,,,vscode-latex%2Fvscode-with-extension.avif,233860,GetObject,1385315732671155,,,,https://cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,,68B5E97978A51C35354AF585,676,/vscode-latex/vscode-with-extension.avif HTTP/1.1,-,233860,33,-,31,NotSign,,,,,,-,-,02/Sep/2025:02:44:09,"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",-,172563320,0
log_dispatch,1756752249,oss_access_log,-,-,-,cyp0633-blogasset,oss-cn-qingdao-h,standard,,,46.62.169.27,-,144022,-,-,,-,-,,cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,GET,200,https,,,true,,,,,,,windows-11-bugs%2Fwin11bug21.avif,144022,GetObject,1385315732671155,,,,https://cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,,68B5E9794A68943931DED990,668,/windows-11-bugs/win11bug21.avif HTTP/1.1,-,144022,31,-,30,NotSign,,,,,,-,-,02/Sep/2025:02:44:09,"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",-,172563320,0
log_dispatch,1756752249,oss_access_log,-,-,-,cyp0633-blogasset,oss-cn-qingdao-h,standard,,,46.62.169.27,-,158049,-,-,,-,-,,cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,GET,200,https,,,true,,,,,,,wild-chicken-university%2Ftianyancha.avif,158049,GetObject,1385315732671155,,,,https://cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,,68B5E9798F796838331F52B9,676,/wild-chicken-university/tianyancha.avif HTTP/1.1,-,158049,30,-,27,NotSign,,,,,,-,-,02/Sep/2025:02:44:09,"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",-,172563320,0
log_dispatch,1756752249,oss_access_log,-,-,-,cyp0633-blogasset,oss-cn-qingdao-h,standard,,,46.62.169.27,-,28216,-,-,,-,-,,cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,GET,200,https,,,true,,,,,,,vmware-ubuntu-12.04%2Faccount.avif,28216,GetObject,1385315732671155,,,,https://cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,,68B5E9792E174C34349AADA1,669,/vmware-ubuntu-12.04/account.avif HTTP/1.1,-,28216,33,-,32,NotSign,,,,,,-,-,02/Sep/2025:02:44:09,"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",-,172563320,0
log_dispatch,1756752249,oss_access_log,-,-,-,cyp0633-blogasset,oss-cn-qingdao-h,standard,,,46.62.169.27,-,55299,-,-,,-,-,,cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,GET,200,https,,,true,,,,,,,windows-11-21996-leaked%2Fstart-menu.avif,55299,GetObject,1385315732671155,,,,https://cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,,68B5E979AF9A9630396238AC,676,/windows-11-21996-leaked/start-menu.avif HTTP/1.1,-,55299,27,-,26,NotSign,,,,,,-,-,02/Sep/2025:02:44:09,"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",-,172563320,0
log_dispatch,1756752249,oss_access_log,-,-,-,cyp0633-blogasset,oss-cn-qingdao-h,standard,,,46.62.169.27,-,14143,-,-,,-,-,,cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,GET,200,https,,,true,,,,,,,windows-11-21996-leaked%2Foobe4.avif,14143,GetObject,1385315732671155,,,,https://cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,,68B5E979AF9A9635306338AC,671,/windows-11-21996-leaked/oobe4.avif HTTP/1.1,-,14143,33,-,32,NotSign,,,,,,-,-,02/Sep/2025:02:44:09,"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",-,172563320,0
log_dispatch,1756752249,oss_access_log,-,-,-,cyp0633-blogasset,oss-cn-qingdao-h,standard,,,46.62.169.27,-,20354,-,-,,-,-,,cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,GET,200,https,,,true,,,,,,,windows-11-21996-leaked%2Fwindows-10-calendar.avif,20354,GetObject,1385315732671155,,,,https://cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,,68B5E9798D51EC3531998676,685,/windows-11-21996-leaked/windows-10-calendar.avif HTTP/1.1,-,20354,34,-,34,NotSign,,,,,,-,-,02/Sep/2025:02:44:09,"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",-,172563320,0
log_dispatch,1756752249,oss_access_log,-,-,-,cyp0633-blogasset,oss-cn-qingdao-h,standard,,,46.62.169.27,-,32798,-,-,,-,-,,cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,GET,200,https,,,true,,,,,,,windows-11-21996-leaked%2Foobe5.avif,32798,GetObject,1385315732671155,,,,https://cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,,68B5E9798D51EC3230A48676,671,/windows-11-21996-leaked/oobe5.avif HTTP/1.1,-,32798,32,-,30,NotSign,,,,,,-,-,02/Sep/2025:02:44:09,"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",-,172563320,0
log_dispatch,1756752249,oss_access_log,-,-,-,cyp0633-blogasset,oss-cn-qingdao-h,standard,,,46.62.169.27,-,61159,-,-,,-,-,,cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,GET,200,https,,,true,,,,,,,windows-11-bugs%2Fwin11bug5.avif,61159,GetObject,1385315732671155,,,,https://cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,,68B5E979C759F63035B22080,667,/windows-11-bugs/win11bug5.avif HTTP/1.1,-,61159,23,-,18,NotSign,,,,,,-,-,02/Sep/2025:02:44:09,"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",-,172563320,0
log_dispatch,1756752249,oss_access_log,-,-,-,cyp0633-blogasset,oss-cn-qingdao-h,standard,,,46.62.169.27,-,199772,-,-,,-,-,,cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,GET,200,https,,,true,,,,,,,nuc-homelab%2Fsiyuan.avif,199772,GetObject,1385315732671155,,,,https://cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,,68B5E979C70CA13536A34EB1,660,/nuc-homelab/siyuan.avif HTTP/1.1,-,199772,45,-,44,NotSign,,,,,,-,-,02/Sep/2025:02:44:09,"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",-,172563320,0
log_dispatch,1756752249,oss_access_log,-,-,-,cyp0633-blogasset,oss-cn-qingdao-h,standard,,,46.62.169.27,-,41757,-,-,,-,-,,cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,GET,200,https,,,true,,,,,,,csapp-bufferlab%2Fstackframe.avif,41757,GetObject,1385315732671155,,,,https://cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com,,68B5E979C70CA13931A94EB1,668,/csapp-bufferlab/stackframe.avif HTTP/1.1,-,41757,67,-,66,NotSign,,,,,,-,-,02/Sep/2025:02:44:09,"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",-,172563320,0

在开启日志后 5 天的时间里,OSS 总共有 10217 次访问,而其中:

  • 有一个 IP 46.62.169.27 猜出了我的 Referer 白名单,访问了 5571 次,其中最多的一个小时访问了 376 次;
  • 有至少 7 个 GCP 的 IP 带着 Thumbor 的 User-Agent 和空 Referer 访问了 1451 次,鉴于我并不用这个软件,应该是被盗图了;
  • 还有一些其他的可疑访问,但数量较少。

为了防止进一步当冤大头,我再次开始了 vibe。ChatGPT 给我提供了一个方案:利用 Netlify Functions 给图片签名,一次访问签整个页面的图片,然后用户使用带签名的 URL 访问 OSS。OSS 可以设为私有读,从而防止被猜出 OSS 白名单;签名 TTL 可以设置得很小,以防止损失过大。以下是基于 Hugo 完整的代码,虽然全是 vibe 的,但证明有效。

netlify/functions/sign-oss.js,在 Netlify 服务器上运行,用于访问阿里服务器进行签名(同时要安装 ali-oss NPM 包):

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
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
'use strict';

const OSS = require('ali-oss');

// Allow all by default for a dedicated bucket; override via OSS_ALLOWED_PREFIXES
const RAW_PREFIXES = process.env.OSS_ALLOWED_PREFIXES;
const ALLOWED_PREFIXES = (RAW_PREFIXES && RAW_PREFIXES.trim().length > 0 ? RAW_PREFIXES : '*')
 .split(',').map(s => s.trim()).filter(Boolean);

function isAllowedKey(key) {
 if (!/^[\w\-./]+$/.test(key)) return false;
 if (ALLOWED_PREFIXES.includes('*')) return true;
 return ALLOWED_PREFIXES.some(p => key.startsWith(p));
}

function parseAllowedOrigins() {
 return (process.env.ALLOWED_ORIGINS || '')
  .split(',')
  .map(s => s.trim())
  .filter(Boolean);
}

const client = new OSS({
 region: process.env.OSS_REGION,
 accessKeyId: process.env.OSS_ACCESS_KEY_ID,
 accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
 bucket: process.env.OSS_BUCKET
});

exports.handler = async (event) => {
 try {
  const method = (event.httpMethod || 'GET').toUpperCase();
  const headers = event.headers || {};
  const origin = headers.origin || headers.Origin || '';
  const allowedOrigins = parseAllowedOrigins();
  const acao = allowedOrigins.length ? (allowedOrigins.includes(origin) ? origin : '*') : '*';

  if (method === 'OPTIONS') {
   return {
    statusCode: 204,
    headers: {
     'access-control-allow-origin': acao,
     'access-control-allow-methods': 'GET,POST,OPTIONS',
     'access-control-allow-headers': 'content-type',
     'cache-control': 'private, max-age=60'
    }
   };
  }

  if (method !== 'GET' && method !== 'POST') {
   return { statusCode: 405, body: 'Method Not Allowed' };
  }

  if (allowedOrigins.length && !allowedOrigins.includes(origin)) {
   return { statusCode: 403, body: 'Forbidden', headers: { 'access-control-allow-origin': acao } };
  }

  let keys = [], ttl = 600;
  if (method === 'GET') {
   const qs = event.queryStringParameters || {};
   const rawKeys = qs.keys || '';
   keys = String(rawKeys).split(',').map(s => s.trim()).filter(Boolean);
   ttl = parseInt(qs.ttl || '600', 10);
  } else {
   let bodyObj = {};
   if (event.body) {
    try {
     const text = event.isBase64Encoded ? Buffer.from(event.body, 'base64').toString('utf8') : event.body;
     bodyObj = JSON.parse(text);
    } catch (_) {
     bodyObj = {};
    }
   }
   keys = Array.isArray(bodyObj.keys) ? bodyObj.keys : [];
   ttl = parseInt(bodyObj.ttl ?? 600, 10);
  }

  ttl = Math.min(Math.max(ttl, 30), 3600);
  const sanitized = keys.filter(isAllowedKey);
  if (sanitized.length === 0) {
   return {
    statusCode: 400,
    headers: { 'access-control-allow-origin': acao },
    body: JSON.stringify({ error: 'No valid keys' })
   };
  }

  const now = Math.floor(Date.now() / 1000);
  const urls = await Promise.all(sanitized.map(async (key) => {
   const url = client.signatureUrl(key, { expires: ttl, method: 'GET' });
   return { key, url, exp: now + ttl };
  }));

  return {
   statusCode: 200,
   headers: {
    'content-type': 'application/json; charset=utf-8',
    'cache-control': 'private, max-age=60',
    'access-control-allow-origin': acao,
    'access-control-allow-methods': 'GET,POST,OPTIONS',
    'access-control-allow-headers': 'content-type'
   },
   body: JSON.stringify({ urls })
  };
 } catch (e) {
  return { statusCode: 500, body: JSON.stringify({ error: 'Server error' }) };
 }
};

netlify.toml 添加配置,指定函数目录:

toml
1
2
3
4
5
[build]
  functions = "netlify/functions"

[functions]
  node_bundler = "esbuild"

config.toml,定义 OSS 前缀链接:

toml
1
2
3
4
5
[params.oss]
hosts = [
  "https://cyp0633-blogasset.oss-cn-qingdao.aliyuncs.com/",
  "https://cyp0633-blogasset.cn-qingdao.oss.aliyuncs.com/"
]

layouts/_default/_markup/render-image.html,将具有上述前缀的链接替换为 data-oss-key

html
 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
{{- /*
 Override: detect OSS remote images and emit data-oss-key for client signing,
 other images fall back to theme default behavior.
*/ -}}
{{- $dest := .Destination | safeURL -}}
{{- $alt := .PlainText | safeHTML -}}

{{- /* Determine if remote and hosted on OSS */ -}}
{{- $isRemote := or (hasPrefix $dest "http://") (hasPrefix $dest "https://") -}}
{{- $ossHosts := site.Params.oss.hosts | default (slice) -}}
{{- $matchOss := false -}}
{{- if $isRemote -}}
 {{- if gt (len $ossHosts) 0 -}}
  {{- range $ossHosts -}}
   {{- if hasPrefix $dest . -}}
    {{- $matchOss = true -}}
   {{- end -}}
  {{- end -}}
 {{- else -}}
  {{- /* Fallback: aliyun OSS domain heuristic (aliyuncs.com and aliyun.com) */ -}}
  {{- if or (findRE `^https?://[^/]*(aliyuncs\.com)/` $dest) (findRE `^https?://[^/]*(aliyun\.com)/` $dest) -}}
   {{- $matchOss = true -}}
  {{- end -}}
 {{- end -}}
{{- end -}}

{{- if and $isRemote $matchOss -}}
 {{- /* Extract object key (path without leading slash) */ -}}
 {{- $key := (replaceRE `^https?://[^/]*/` `` $dest) -}}
 {{- $key = (replaceRE `^/` `` $key) -}}
 {{- $placeholder := `data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==` -}}
 <figure style="max-width: 80%; margin: auto;">
  <img data-oss-key="{{ $key }}" src="{{ $placeholder | safeURL }}" loading="lazy" {{ with $alt }} alt="{{ . }}" {{ end }}
   style="width: 100%; height: auto; max-height: 70vh; object-fit: contain;">
  {{ if .Title }}
  <figcaption>{{ .Title | markdownify }}</figcaption>
  {{ end }}
 </figure>
{{- else -}}
 {{- /* Default theme behavior for local or non-OSS remote images */ -}}
 {{- $image := .Page.Resources.GetMatch (printf "%s" (.Destination | safeURL)) -}}
 {{- $Permalink := .Destination | relURL | safeURL -}}
 <figure style="max-width: 80%; margin: auto;">
  <img src="{{ $Permalink }}" loading="lazy" {{ with $alt }} alt="{{ . }}" {{ end }}
   style="width: 100%; height: auto; max-height: 70vh; object-fit: contain;">
  {{ if .Title }}
  <figcaption>{{ .Title | markdownify }}</figcaption>
  {{ end }}
 </figure>
{{- end -}}

assets/ts/custom.ts,用于收集 data-oss-key 请求签名:

typescript
 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
type SignedEntry = { key: string; url: string; exp: number };

function collectOssKeys(): string[] {
 const imgs = Array.from(document.querySelectorAll<HTMLImageElement>('img[data-oss-key]'));
 const keys = imgs
  .map((img) => img.dataset.ossKey || '')
  .filter((k) => k.length > 0);
 return Array.from(new Set(keys));
}

async function fetchSignedUrls(keys: string[], ttlSeconds = 600): Promise<Map<string, string>> {
 const endpoint = '/.netlify/functions/sign-oss';
 const url = `${endpoint}?ttl=${encodeURIComponent(String(ttlSeconds))}&keys=${encodeURIComponent(keys.join(','))}`;
 const res = await fetch(url, { credentials: 'omit', cache: 'no-store' });
 if (!res.ok) return new Map();
 const data = (await res.json()) as { urls?: SignedEntry[] };
 const map = new Map<string, string>();
 (data.urls || []).forEach((u) => {
  if (u && u.key && u.url) map.set(u.key, u.url);
 });
 return map;
}

function applySignedUrls(map: Map<string, string>): void {
 document.querySelectorAll<HTMLImageElement>('img[data-oss-key]').forEach((img) => {
  const key = img.dataset.ossKey || '';
  const signed = key ? map.get(key) : undefined;
  if (signed) {
   img.src = signed;
   img.removeAttribute('data-oss-key');
  }
 });
}

async function initOssSigning(): Promise<void> {
 const keys = collectOssKeys();
 if (keys.length === 0) return;
 try {
  const map = await fetchSignedUrls(keys, 600);
  applySignedUrls(map);
 } catch {
  // no-op
 }
}

if (document.readyState === 'loading') {
 document.addEventListener('DOMContentLoaded', () => {
  void initOssSigning();
 });
} else {
 void initOssSigning();
}

另外还需要在 Netlify 上设置环境变量:

  • ALLOWED_PREFIXES:如果所有图片都在同一个目录下可以使用;
  • OSS_REGION:OSS 地域,例如 oss-cn-qingdao
  • OSS_BUCKET:OSS Bucket 名称,例如 cyp0633-blogasset
  • OSS_ACCESS_KEY_IDOSS_ACCESS_KEY_SECRET:访问密钥。

就行了。构建时 Hugo 会自动将 OSS 链接替换为 key,只能在访问时签名,签名只能用于特定的文件,且有效期有限;即使 key 特征明显,也无法直接访问 OSS。

在部署这个方案之后,收到的成效非常显著:

  • 46.62.169.27 再也没有出现过;
  • 盗图的 Thumbor 也没有再出现过;
  • 搜索引擎索引(包括 Bingbot、Googlebot、ChatGPT 搜索和 Petalbot 等)正常访问。

按小时的访问量统计
按小时的访问量统计

Top IP 访问量统计
Top IP 访问量统计

不出意外的话,你也能访问这个页面的图片内容,如果不能的话,请你在评论区留言,除非你的评论区也加载不出来:)

许可证:CC BY-SA 4.0
最后更新于 2025 年 9 月 7 日 17:41
Artalk ErrorFailed to load comments
TypeError: Failed to fetch


Retry