2026-04-28 21:42:18
今天照例用手机登录服务器日常维护一下,顺手敲下大家倒背如流的 sudo apt update && sudo apt upgrade,准备给系统更个新。结果直接被兜头浇了一盆冷水:
tjsky@vm1234:~# sudo apt update && sudo apt upgrade
Reading package lists... Done
E: Could not get lock /var/lib/apt/lists/lock. It is held by process 31896 (apt-get)
N: Be aware that removing the lock file is not a solution and may break your system.
E: Unable to lock directory /var/lib/apt/lists/
我还是头一次遇到这种 Ubuntu apt update 报错的事情。于是只好起身打开电脑,在网上一搜,中文教程里都让我简单粗暴地执行 sudo rm -rf /var/lib/apt/lists/lock直接把带锁文件给删了,作为一个曾经干出《一行 rm -rf 删光了我的博客》,3年多的博客被炸到什么都不剩的人,对rm命令那是相当慎重的,果断找 AI 学习了一番相关知识。
果然,又是一如既往的:垃圾教程污染中文互联网!
而且其实系统提示里已经苦口婆心地警告过我了:removing the lock file is not a solution。强删锁文件和不弹出就直接热拔插 U 盘有啥区别。虽然确实能强行解决报错,但鬼知道文件系统里会变成什么样。
那既然系统说锁现在是被 PID=31896 进程占用了,那我就来看看它到底在干嘛:
tjsky@vm1234:~# ps -f -p 31896
UID PID PPID C STIME TTY TIME CMD
root 31896 31868 0 Apr18 ? 00:00:50 apt-get -qq -y update
好家伙, STIME(启动时间)赫然写着 Apr18!。今天可是Apr28! 已经在后台默默“挂机”了一个多星期了!(估计可能碰上了网络波动或者镜像源超时,然后就卡死了?)
看眼这进程是什么东西启动的,能不能干掉(虽然已经大致猜到是什么了)
tjsky@vm1234:~# pstree -aps 31896
systemd,1
└─systemd,1055
└─apt.systemd.dai,31868 /usr/lib/apt/apt.systemd.daily update
└─apt-get,31896 -qq -y update
额,这不是系统默认的每天自动刷新软件源的定时任务嘛,那就简单了,既然应该不是在下载安装中卡死,那直接干死就行:
tjsky@vm1234:~# sudo kill -9 31896
# 把它的父进程也送走
tjsky@vm1234:~# sudo kill -9 31868
# 虽然按说应该是卡在刷新软件列表了,不过以防万一,还是修复一下可能存在的安装状态
tjsky@vm1234:~# sudo dpkg --configure -a
没有任何输出直接回到命令提示符,不错,毕竟在 Linux 的世界里,有一条著名的“Unix 哲学”:没有消息就是好消息。
作为一个好奇心满满的折腾党,只是把僵尸进程干死了可不行,我要去看看这 10 天到底发生了什么。
首先,去翻 apt 自己的记录 /var/log/apt/history.log,结果发现 4 月 18 日这天没有任何记录。
诶,我去,不可能啊,没记录?那4月18号那天是什么东西启动的更新,见鬼了?
又往前翻了几天的日志,转念一想,哦,卡死的命令是 update,它只是去远程拉取最新的软件清单索引,并没有实际 install 或 upgrade 任何具体的软件包,搞不好这时候确实不写入history.log的,毕竟之前也没有看到过update的记录。
那既然 apt 的日志里没记录,那就去问问系统大管家 systemd呗。
tjsky@vm1234:~# sudo journalctl --since "2026-04-18" --until "2026-04-19" | grep -i apt
果然
Apr 18 06:29:39 vm1234 systemd[1]: Starting Daily apt upgrade and clean activities...
Apr 18 06:29:42 vm1234 systemd[1]: Finished Daily apt upgrade and clean activities.
# 上面是早上的正常升级检查,几秒钟就顺利跑完了。重点在下面:
Apr 18 14:43:39 vm1234 systemd[1]: Starting Daily apt download activities...
下午 14:43 分触发的 apt-daily.service(后台更新软件源),只有 Starting,没有 Finished。进程一直拿着 apt 的进程锁占着茅坑不拉屎,导致这 10 天内所有的后续更新都被堵在了门外。
查明真相并清理完后,我信心满满地再次执行了手动更新命令。结果,屏幕上又开始疯狂刷屏:
Waiting for cache lock: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 47902 (unattended-upgr)
Waiting for cache lock: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 47902 (unattended-upgr)
...
不是,这难道又卡死了?我不是刚干死卡死的每日更新嘛,这又是啥东西在为难我。
仔细了一眼,看占用锁的进程变成了 unattended-upgr。于是就去查了一下这是个啥玩意,AI告诉我:“这是 Ubuntu 系统自带的后台自动安全更新服务。”
合着我手动更新的时候,恰好撞上了系统正在后台默默打安全补丁,用了快10年的Ubuntu,还是头一次碰到系统自动安全更新的😂。
那就只能等了,不过幸好也就等了1分钟不到,日志画风一转:
Waiting for cache lock: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 59297 (apt-get)
Reading package lists... Done
Building dependency tree... Done
...
锁被释放了,熟悉的 Do you want to continue? [Y/n] 终于弹了出来。按Y,回车,收工!
要是你们碰到 /var/lib/apt/lists/lock 被占用,或者 apt 进程死锁,记住啊:
rm 搞怕了,直接把自己3年多的博客全删了的惨状还历历在目,当时吓的血都凉了。kill进程了,应该先journalctl 看看系统最后在干啥,判断一下到底是真死锁了还是在跑大任务,然后再决定是否直接杀进程。Waiting for cache lock,不慌,系统会帮着把队列排得明明白白的。毕竟Ubuntu 24.04 的代号是 Noble Numbat (高贵的食蚁兽)体现了系统的成熟与可靠(吗?)。本文 Ubuntu 更新“卡死”惊魂记:揪出占用 apt 锁的“隐形罪魁祸首”! 最初发表于 秋风于渭水。
2026-04-10 08:53:35
1月份的时候,我实在受够了罗技驱动的臃肿,写了篇《告别 Logitech Options+ 臃肿!罗技驱动精简瘦身与替代全攻略》,提供了一个可以按需开启功能的安装脚本「tjsky/logi-options-plus-mini」。没想到大家苦罗技久矣,这项目发布后不仅大家很是捧场,还被某知名科技自媒体翻牌子引用,成了我 GitHub 上 Stars 数量最高的项目😂
不过开心归开心,这方法只能说是治标不治本。虽然利用该项目可以阻止多余功能被启用,但所需的依赖文件依然稳如泰山地躺在硬盘里。再配合罗技 Options+ 那每周雷打不动、重新下载小几百 MB 文件的“特性”(Bug),而且还从!不!删!除!旧副本(拿用户硬盘当自家的垃圾场是不)。实测下来,即使精简到了极致,用了三个月后,这玩意儿的文件体积依然原地爆炸,膨胀到了 2GB!

为了彻底干掉这个“毒瘤”,3 个月后我给大家搞来了两套终极解法:官方“妥协版” 和 非官方的“真香魔法”。
对于电竞比赛电脑、隔离网络的商业电脑,是需要一个绝对不联网的驱动来保证竞技公平和商业机密的。所以罗技其实偷偷藏了一手:Logitech Options+ 离线版。
所以,这个方法只适合那些要官方背书、追求“原厂服务质量”的用户(虽然罗技最近的信用值,怕是连共享单车都扫不开了吧)。
相比官方的臃肿。Mouser 是一个极其轻量、开源、完全本地运行的 Options+ 替代品,专门用来对罗技 HID++ 鼠标进行按键和手势的重映射。目前对 MX Master 系列的体验最为丝滑,同时对其他罗技型号也提供了早期识别和通用 UI。
划重点:无需云端、无需罗技账号、纯本地运行! 安装包体积只有 50 MB 的小工具,把官方那个 1.1GB 的庞大驱动按在地上摩擦。


| 鼠标型号 | 自动 HID++ 检测 | UI 界面 |
|---|---|---|
| MX Master 4 / 3S / 3 / 2S / MX Master | 支持 |
MX Master 系列专用界面 |
| MX Anywhere 3S / 3 / 2S | 支持 | 通用界面,实验性支持界面(需手动切换) |
| MX Vertical | 支持 | 通用界面,实验性支持页面(需手动切换) |
| 其他罗技 HID++ 鼠标 | 部分支持(基于PID/名称) | 通用界面 |
Mouser.exe(Windows) / Mouser.app(macOS) / 终端运行 ./Mouser(Linux)。(注:macOS 会管你要两个隐私权限,用来获取鼠标状态;Linux 需要给 /dev/input/event* 的读取权限和 /dev/uinput 的写入权限。)
不过话说回来,macOS 用户其实不如直接去用 SteerMouse 或者 BesterMouse 虽然需要掏钱,但好用的多,而且鼠标的平滑滚动更接近苹果原生体验。至于 Linux 用户,LogiOps 才是你最好的选择。
%APPDATA%\Mouser(Windows)、~/Library/Application Support/Mouser(macOS)、~/.config/Mouser(Linux)。免责声明:Mouser 是个第三方开发的开源项目,跟罗技(Logitech)官方没有半毛钱关系,“Logitech”、“MX Master”和“Options+” 都是 Logitech International S.A. 的注册商标。(求生欲拉满的博主)
本文 彻底告别 Options+ 臃肿!开源驱动 Mouser 实测:这才是罗技鼠标该用的驱动 最初发表于 秋风于渭水。
2026-03-16 15:25:47
免责声明: 本文仅作为网络安全技术探讨与供应链安全案例分析。文中所引用的测试数据及文件路径均来自公开网络渠道及官方公开发布的软件安装包,不涉及任何非法逆向工程、破解或入侵行为。文章旨在提醒广大开发者重视密钥管理规范,并建议相关用户注意潜在的网络通信风险。若涉及厂商已发布官方修复公告,请以官方信息为准。
近日,某安全社区内有研究人员指出,在 某数字 公司近期发布的 AI Agent 产品 “安全龙虾”(基于 OpenClaw)的公开安装包中,疑似意外包含了其官方泛域名证书的私钥文件。
这一发现引发了技术圈对于客户端打包规范以及 WebPKI 证书吊销机制的广泛讨论。本文将从技术角度对该事件进行客观验证与风险分析。
根据网络社区公开的信息,在官方提供的软件目录树中,存在一个名为 credentials 的明文文件夹,具体路径如下:/path/to/namiclaw/components/Openclaw/openclaw.7z/credentials
该目录内包含了 *.myc***.36X.cn 的 Wildcard DV(泛域名)证书及其对应的私钥。
为了验证该私钥与证书的匹配性,我们可以通过标准的 OpenSSL 工具分别提取私钥和证书的 Modulus(模数)进行 MD5 哈希比对:
> openssl rsa -modulus -noout -in myc***.36X.cn.key | openssl md5
MD5(stdin)= 446097b7674080186a469ecb0945f5af
> openssl x509 -modulus -noout -in myc***.36X.cn.crt | openssl md5
MD5(stdin)= 446097b7674080186a469ecb0945f5af
输出结果显示,两者的 MD5 指纹完全一致,在技术上证实了该 .key 文件确为对应官方泛域名证书的有效私钥。
将泛域名私钥直接打包进公开发布的客户端,在安全工程实践中属于较高风险的配置失误。其实际影响主要体现在以下几个维度:
*.my****.36X.cn 旗下的任何服务端节点。由于客户端和系统会信任该合法证书,HTTPS 的加密隧道形同虚设,流量可被实时解密。
敏感凭据暴露: 考虑到 OpenClaw 是一款 AI Agent 工具,用户通常会在其中配置各类大语言模型(如 OpenAI, Claude 等)的 API Key。若发生上述中间人劫持,这些高价值的通信凭据存在被明文截获的理论风险。
供应链劫持可能: 若该客户端的某些更新或指令下发机制依赖于该子域名的 HTTPS 验证,恶意攻击者可能利用伪造的服务器向客户端下发未经授权的指令或恶意代码。
除了开发环节的打包疏漏,本次事件在证书管理(PKI)层面的响应时间也值得关注。
根据公开的证书透明度(CT)日志(如 crt.sh ID: 24937759962),该证书由 WoTrus(沃通)于 2026年3月12日签发。截至本文撰写之时(距离私钥被公开讨论已接近1天),该证书状态依然为 Valid(有效)。
参照 CA/Browser Forum Baseline Requirements 的行业通行规定(4.9.1.1 章节),当 CA 机构意识到证书私钥可能已遭泄露(Compromised)时,应当在 24 小时内执行吊销(Revoke)操作。此次事件中响应的延迟,暴露出相关主体在漏洞响应(IR)流程上可能存在一定的滞后,这也给 WoTrus 带来了潜在的合规审查压力。
更新 2026-03-16 08:07:16 UTC 时刻,证书 OCSP 显示 Revoked (吊销)
不过鉴于大部分浏览器对无法访问 OCSP 验证服务器时会采取“软失败”配置,仅靠 OCSP 证书吊销验证并不能有效防御中间人攻击(能做中间人攻击的人,顺手拦截掉 OCSP 流量也很容易)
对于安全开发生命周期(SDL)而言,高等级的泛域名私钥应当严格存放在硬件安全模块(HSM)或专用的密钥管理系统(KMS)中,开发人员本应无法接触到私钥本体。即使出于这样那样的原因,允许开发人员直接接触私钥,也应配合 CI/CD 流水线的 Secret 扫描机制,防止重要凭据硬编码或被意外打包进公开发布版本。
出于谨慎起见,建议在官方发布包含新证书的修复版本并吊销旧证书之前,暂时避免在存在不可信网络环境的设备上使用该特定版本的客户端。同时,若曾在其中输入过高价值的 API 密钥,建议前往对应的服务商后台进行重置(Regenerate)操作,以确保资产安全。
其实谁都有写出 Bug 的时候,但这波操作实在有点过于草台班子了,不要为了快的抢先发布,而跳过开发流程的安全检查。这次幸亏只是一个*.myc***.36X.cn,要是不小心泄露了更核心的*.36X.cn的私钥,整个公司所有软件的安全信任体系可就全炸了。
业务因失误将内部域名证书打包到安装包里,证书对应域名是
*.myclaw.360.cn,实际解析地址是127.0.0.1本地回环地址,该证书仅在本地使用,不会对外提供任何服务。
发现问题后 360 安全团队立即申请吊销这份证书,目前证书已经失效,无法再用于任何合法的 HTTPS 加密通信,普通用户也不会受到任何影响。
公司已启动内部排查流程,将进一步优化安全管理机制,防范类似疏漏再次发生。
本文 【资讯】离谱!某数字安全大厂 AI 客户端竟“附赠”自己泛域名的私钥? 最初发表于 秋风于渭水。
2026-03-13 18:03:59
没想到,我最终还是被非中文垃圾评论逼疯了,我的小破站也有被垃圾评论淹没的一天。
最近(这半年)国外的 SPAM Bot 像是冲业绩一样,对着我的评论区狂轰滥炸。
给你们看一眼后台 Akismet 的统计数据:

虽然上个月 Akismet 默默帮我吃掉了 1700 多条纯英文的博彩和 SEO 垃圾评论,并且正确率高达 99.9%,在近 7000 条评论中,漏网的 SPAM 评论数量为 0,假阳性(将正常评论误判为 SPAM)的数量也只有 5 条,可以说 Akismet 工作得非常好。
但问题是,这些被拦截的非中文垃圾评论,会全堆在后台的“垃圾”列表里。之前这个数字涨得还比较慢,我基本每周点一次「清空」,最近这个数字涨得实在太快了,简直是在疯狂折磨我的强迫症!搞得我现在只要进后台就要手动点“清空垃圾”。
上个月一直在折腾前端机反代的事情(详见《忍痛割爱“负优化”腾讯云!年付不到200元,我换到了这台“真香”的香港前端机》)。最近基础的缓存问题、前端轻量化 WAF 都搞得差不多了,我决定从源头上给这帮瞎撞的机器人一点颜色看看。
解决思路很清楚:通过后端的 PHP 或前端的 JS,检查提交的评论内容,禁止中文数量太少的评论和直接 POST 到接口的评论。我印象中不止一位博主写过类似的文章,可惜我在 RSS 阅读器里搜了几下,什么都没找到。
遇到问题先“面向 Google 编程”,没必要重复造轮子嘛,搜“WordPress 禁止非中文评论”,不出意外地,满屏的 CV 工程师都在发同一段祖传代码(以及它的徒子徒孙们),各种代码之间的不同之处,无非是写了更多的正则去匹配日语、韩语等语言,改了点报错文本的措辞罢了,它们的核心都是下边这个代码:
// 屏蔽非中文评论 (注意,这个代码超垃圾,千万别用!)
function refused_spam_comments( $comment_data ){
$pattern = '/[一-龥]/u';
if(!preg_match($pattern,$comment_data['comment_content'])){
err( "我擦,你竟然是歪果仁!看不懂你的评论..." );
}
return( $comment_data );
}
add_filter('preprocess_comment','refused_spam_comments');
我差点没一口老血喷出来,这都啥祖传垃圾代码啊,槽点密集到无可复加,大家且听我逐一吐槽:
/[一-龥]/u,先不说这个正则区间里混进了多少不是中文的奇怪东西。就这写法,纯纯是化石级的写法,我印象里只有 2000 年之前就接触编程的老古董会这样写,因为当年的很多教材就这样写的,他们习惯了。这段核心代码出现的年代绝对不可能早于 2010 年。在 2026 年的当下,匹配中文就算不用 /\p{Unified_Ideograph}/u(匹配中文)或 /\p{Script=Han}/u(匹配中文和中文标点),也要写成 /[\u4e00-\u9fa5]/u 吧(兼容 IE11 等老浏览器)。我寻思,JavaScript 从最初版本 (1997年)就开始支持 \uXXXX了吧?。err( "我擦,你竟然是歪果仁!可惜博主的英文太烂,看不懂你的评论,学会汉字再来评论吧..." ); 这个语气,就让我仿佛回到了高中时代的 QQ 空间。err() 函数是哪里冒出来的啊?WordPress 里根本没有这个函数吧!经过我一番 AI 检索,哦,这是当年某个流行主题的自定义函数,主题没有WordPress的原生评论系统,而是用自己写了一个 comments-ajax.php 作为评论系统,使用err() 实现前端报错。因此这个最后的钩子 add_filter('preprocess_comment','refused_spam_comments'); 存在同样的问题,因为代码中没有使用 WordPress 原生的 wp_die() 函数来终止程序,而且咱们也不用那个主题,自然也就没有 err() 函数,部署上去妥妥会炸。既然“面向 Google 编程”不成功,那就自己造轮子呗。不过这时候我灵光一闪,其实我的需求并不是完全靠自己解决垃圾评论的过滤问题,而是尽量在评论到达 Akismet 之前,就检测出对方是不是机器人。
所以我决定简单点,一贯不想增加插件负担的我,在后端用 PHP 给评论表单偷偷塞一个隐藏的当前时间戳。访客提交时一减,如果发现他从打开页面到点提交用时小于 6 秒,绝对是机器,当场弄死。
// 基于 PHP 的时间检测代码片段
// 1. 在前端评论表单注入一个“隐形时间戳”
function tjsky_add_comment_time_trap() {
echo '<input type="hidden" name="comment_render_time" value="' . time() . '">';
}
// 2. 检测对比打开页面后和提交时的时间戳
if (isset($_POST['comment_render_time'])) {
$render_time = (int)$_POST['comment_render_time'];
$time_spent = time() - $render_time;
// 3. 如果从打开页面到点提交不到 6 秒,绝对是机器,弄死。
if ($time_spent < 6) {
tjsky_reject_comment();
}
} else {
// 4. 如果连隐藏的时间戳都没有,说明是直接往接口 POST 数据的,更要弄死。
tjsky_reject_comment();
}
兴冲冲部署上去了,过了一天我发现:怎么没效果啊?思考良久后我一拍大腿反应过来了。我他喵的现在是香港前端机 Nginx 反代并缓存加速 + 后端机的技术路线了,不是原来的前端机端口透传方案。
也就是说,访客看到的页面,早就被 Nginx 缓存在香港节点了!PHP 输出的那个隐藏时间戳,会永远停留在缓存生成的那一秒。一个爬虫过来抓取页面并 POST,后端一算:当前时间减去缓存的时间戳,好家伙,早超过 6 秒了,直接判定为“人类”,完美绕过陷阱!
结论:后端 PHP 动态时间戳在静态缓存架构下完全失效。(折腾半天,小丑竟是我自己)
既然 PHP 生成时间戳行不通,那就只能让访客的浏览器端动态生成了。往前端塞了一小段 JavaScript 脚本,在页面加载完后,用 JS 把时间戳令牌塞进表单。因为很多针对 WordPress 的低级垃圾评论机器人只抓 HTML 分析,并不执行 JS,这一招应该可以完美解决缓存问题了吧。
新版代码特性:
loadTime,而不是等待页面加载完成触发 DOMContentLoaded 再记录时间。这是为了以防网络不好时,页面加载完成和提交评论的时间间隔过短(虽然理论上不至于……但以防万一嘛)。// 基于 JavaScript 的时间检测代码片段
(function() {
// 记录脚本解析时刻的时间戳
var loadTime = Math.floor(Date.now() / 1000);
document.addEventListener('DOMContentLoaded', function() {
// 往评论表单里写入隐形的时间戳
var forms = document.querySelectorAll('form[action*="wp-comments-post.php"], #commentform, #comment-form');
forms.forEach(function(form) {
if (!form.querySelector('input[name="js_spamtrap_token"]')) {
var jsToken = document.createElement('input');
jsToken.type = 'hidden';
jsToken.name = 'js_spamtrap_token';
jsToken.value = 'is_human_0';
form.appendChild(jsToken);
// 在评论提交的瞬间,计算时间差
form.addEventListener('submit', function() {
var submitTime = Math.floor(Date.now() / 1000);
var timeDiff = submitTime - loadTime;
jsToken.value = 'is_human_' + timeDiff;
});
}
});
});
})();
结果:部署上去后测试了 1 天,垃圾评论数量大量减少,大概只有原来 20% 的垃圾评论到达了 Akismet,主要是几个做垃圾 SEO 的评论。不过它们居然绕过了 JS 时间差检测,这倒是有点出乎我的意料。从后台记录的时间差看,它们基本是在打开页面 1~2 分钟后提交的评论。现在的垃圾评论有点先进哦,居然有 JS 运行环境,而且会模拟人类的阅读行为?
得,还是要回到检测非中文评论的路子上来。
首先,我不能设置“只要含有少量中文就放行”的规则,因为我见过会复制我文章标题来伪装的垃圾评论,所以我决定设置为“纯中文字符占比低于 20% 直接拦截”。
写完后,我让 AI 写了个测试脚本,从博客的数据库里拉了 100 条正常评论和 100 条垃圾评论,在真实环境下做测试,结果喜迎再次翻车。
“有效汉字比例小于 20%”的拦截规则,对于生活类博客确实好使,但别忘了这里是一个技术博客(虽然我最近没怎么写教程吧)。
一旦遇到某位老哥跟着我的教程部署/安装代码时报错了,他很有可能会在评论区糊上一大段全英文的报错日志或配置代码,末尾才带了一句:“大佬,报错了,运行后显示这个怎么办”等等很短的一句话。
按照我的规则很容易就直接弹个 403 把友军给厚葬了,这绝对是反人类的。
有人可能会说:那拦截的时候,你直接在报错页面提示他“您的评论中文字符比例过少,请去掉代码重新组织语言”不就行了嘛?
我跟你讲,你可千万别啊!永远不要在报错里给黑客当老师。如果你把具体的规则明明白白写在脸上,灰产作者虽然不会时时刻刻盯着报错看,但回头在脚本里加个正则,随机抓取正文里的中文字符来凑数的案例可是有过的。真正的防御必须是“外松内紧”,统一返回个“缺少有效上下文”的障眼法,让他自己猜去吧。
明确了痛点,我把最终的逻辑梳理成了下面这样:
如果你的 WordPress 博客充斥着大量非中文垃圾评论,并且有前端缓存、CDN、静态化之类的东西,可以试试下边这套经过无数次踩坑打磨出来的无插件防御 SPAM 代码,直接扔到你的 functions.php 里即可:
/**
* 评论区终极防 SPAM 护盾 (适配 Nginx 反代缓存 + 技术博客代码块豁免)
*/
// ==========================================
// 1. 前端 JS 注入:动态计算停留时间,完美绕过页面缓存
// ==========================================
add_action('wp_footer', 'tjsky_add_js_time_trap');
function tjsky_add_js_time_trap() {
if (is_singular() && comments_open()) {
?>
<script type="text/javascript">
(function() {
// 核心魔法 1:只要浏览器解析到这段 JS,立刻记录初始时间(秒)。
// 绝不放在 DOMContentLoaded 里,防止被页面上其他加载卡顿的资源阻塞!
var loadTime = Math.floor(Date.now() / 1000);
document.addEventListener('DOMContentLoaded', function() {
// 暴力兼容各种主题的评论表单
var forms = document.querySelectorAll('form[action*="wp-comments-post.php"], #commentform, #comment-form');
forms.forEach(function(form) {
if (!form.querySelector('input[name="js_spamtrap_token"]')) {
var jsToken = document.createElement('input');
jsToken.type = 'hidden';
jsToken.name = 'js_spamtrap_token';
jsToken.value = 'is_human_0'; // 初始值
form.appendChild(jsToken);
// 核心魔法 2:在表单提交的瞬间,计算时间差并赋值
form.addEventListener('submit', function() {
var submitTime = Math.floor(Date.now() / 1000);
var timeDiff = submitTime - loadTime;
jsToken.value = 'is_human_' + timeDiff;
});
}
});
});
})();
</script>
<?php
}
}
// ==========================================
// 2. 核心拦截逻辑:在写入数据库前进行终极安检
// ==========================================
add_filter('preprocess_comment', 'tjsky_intercept_spam_comments');
function tjsky_intercept_spam_comments($commentdata) {
// 站长自己和 Pingback 机器通知,直接放行
if (is_user_logged_in() || (isset($commentdata['comment_type']) && in_array($commentdata['comment_type'], array('pingback', 'trackback')))) {
return $commentdata;
}
// --- 校验 1:前端 JS 时间陷阱检测 ---
// 如果连标记都没有(直接 POST 的低级爬虫),直接处决
if (!isset($_POST['js_spamtrap_token']) || strpos($_POST['js_spamtrap_token'], 'is_human_') !== 0) {
tjsky_reject_comment();
}
// 提取时间差
$time_spent = (int) str_replace('is_human_', '', $_POST['js_spamtrap_token']);
// 如果打开页面不到 6 秒就点提交,绝对是机器,弄死
if ($time_spent < 6) {
tjsky_reject_comment();
}
$content = $commentdata['comment_content'];
// 算一下原始文本的汉字数量
preg_match_all('/[\x{4e00}-\x{9fa5}]/u', $content, $matches_original);
$original_chinese_count = count($matches_original[0]);
// 看看有没有贴代码的痕迹
$has_code_block = preg_match('/(`|<pre>|<code>)/i', $content);
// 【技术博客灵魂豁免通道】:带了代码块,且好好打字超过了 10 个汉字,直接放行交由 Akismet 审查!
if ($has_code_block && $original_chinese_count >= 10) {
return $commentdata;
}
// --- 校验 2:严格的汉字浓度清洗 ---
// 剥离所有的 URL 链接 (防止垃圾链接凑数)
$content_clean = preg_replace('/https?:\/\/[^\s]+/i', '', $content);
// 剥离多行和单行 Markdown 代码块,以及 HTML 代码标签
$content_clean = preg_replace('/```.*?```/is', '', $content_clean);
$content_clean = preg_replace('/`[^`]*`/i', '', $content_clean);
$content_clean = preg_replace('/<pre.*?>.*?<\/pre>/is', '', $content_clean);
$content_clean = preg_replace('/<code.*?>.*?<\/code>/is', '', $content_clean);
// 剥离空白字符
$content_clean = preg_replace('/\s+/u', '', $content_clean);
$total_len = mb_strlen($content_clean, 'UTF-8');
// 剔除后成空壳了?说明全是链接和空格,处决
if ($total_len === 0) {
tjsky_reject_comment();
}
// 计算剔除代码后的真实汉字占比
preg_match_all('/[\x{4e00}-\x{9fa5}]/u', $content_clean, $matches_clean);
$clean_chinese_count = count($matches_clean[0]);
$ratio = ($clean_chinese_count / $total_len) * 100;
// 绝杀:日常交流文本中文字符少于 2 个,或者占比低于 20%
if ($clean_chinese_count < 2 || $ratio < 20) {
tjsky_reject_comment();
}
return $commentdata;
}
// ==========================================
// 3. 障眼法处决函数 (绝不暴露真实死因)
// ==========================================
function tjsky_reject_comment() {
// 强行返回 403,反向污染爬虫的靶机库
header('HTTP/1.1 403 Forbidden');
header('Status: 403 Forbidden');
$msg = '<div style="max-width: 600px; margin: 50px auto; padding: 20px; border-left: 4px solid #dc3545; background: #f8d7da; color: #721c24; font-family: sans-serif; border-radius: 4px; line-height: 1.6;">';
$msg .= '<strong>⚠️ 提交失败 / Submission Failed:</strong><br><br>';
// 这里使用含糊其辞的报错,防止被针对性突破
$msg .= '系统检测到您的评论存在「格式异常」或「缺少有效上下文」的问题。请修改您的表单并重新组织语言后再试一次。<br>';
$msg .= '<span style="font-size: 13px; color: #856404;">System flagged this comment as irregular or lacking meaningful context. Please rephrase.</span><br><br>';
$msg .= '<a href="javascript:history.back()" style="color: #721c24; text-decoration: underline;">返回修改 / Go back and modify</a>';
$msg .= '</div>';
wp_die($msg, '拦截提示', array('response' => 403));
}
这年头WordPress被针对的太多了吧,防垃圾评论都能防出谍战片的感觉,不光得跟外面的爬虫斗智斗勇,还得防着自家的缓存背刺。有了这套 WordPress 禁止非中文垃圾评论代码,终于不用天天进后台清空垃圾了。代码已经丢上去跑了几天,那个烦人的未读红点也没再隔三差五地蹦出来。(严格说,这 3 天内防御率为 100%)
本文 拒绝祖传垃圾代码!适配前端静态缓存的 WordPress 防垃圾评论纯代码方案(附源码) 最初发表于 秋风于渭水。
2026-03-09 15:46:10
之前腾讯云新加坡轻量,国内访问都是走的「新加坡腾讯云 → 新加坡SingTel(新加坡电信) → AS9929(中国联通工业互联骨干网,即所谓的联通A网) → 广州互联网交换中心 」这种极为优质的线路,任谁不要说一句“腾讯高、联通硬”,要知道联通 A 网的价格那是比电信 CN2 还要贵好几倍的。又没有电信 CN2 那种遇到 DDos 就玩犊子的问题。以我2025年续费的价格每年只要16X的价格来看,十分有性价比。
结果自从腾讯云出了EagleOne和轻量云优化流量包之后,香港和新加坡的线路质量就一落千丈,所有流量要么走TATA绕美,要么走NTT绕日,这俩线路的质量众所周知的爆炸。后果就是,博客的访问速度慢的要死,有些地区的访客甚至告诉我,需要白屏 10 秒以上才能看到内容,完全加载完需要20多秒。


你看看,这新加坡腾讯云的路由能忍?从新加坡直接给我绕美国去了,要知道我这个测试点可是广州联通,不走广州出口走北京出口就挺离谱的,出镜进入TATA线路后,丢包丢的飞起,日常自带 5% 丢包,晚高峰丢包到20%都是常态。晚上随便开个页面都需要好几秒。

虽然从上图的网站测速结果看起来,平均用时也就2秒多还可以,但要知道,图里可不是晚高峰时的测速结果,根据比较公认的说法,2~3秒的加载时长是维持用户体验的底线,图里这个速度已经基本是卡着用户体验的底线了,更不用说到了晚高峰动辄6、7秒甚至10秒的加载时长,顶着这个延迟不关闭标签的访客,那可真是真爱了,所以最好还是能将加载时长控制在1秒左右为好。所以我开始琢磨换一台前端机。
为了解决博客访问慢的问题,我对比了目前主流的几种网站提速优化方案
| 方案 | 优势 | 劣势 |
|---|---|---|
| 后端搬至香港腾讯云 | 1. 延迟通常在 40-60ms。 2. 无需维护两台机器。 3. 搬起来方便 |
1.不能保证速度永远不劣化。 2. 香港机器相对于新加坡稍贵 |
| 换香港机器作为前端机 | 1. 灵活性高,线路不行就换新的前端。 2. 可以玩的花活更多一点 |
1. 香港机器一般都比较贵。 2.现有预算下前端机配置不会很高 |
| Cloudflare 或 EagleOne + 优选 IP | 1. 成本为0,可以白嫖 2. 境外速度无敌好,国内也还可以 |
1. 优选IP的稳定性还是有点问题的 2. 不太符合用户协议,玩脱了就不好了 |
为了换个靠谱的前端,我感觉我基本把市面上有点名气的香港机几乎都看了一遍了(笑哭)…… 挑花眼的同时,也算是踩遍了各种奇葩设定的坑。直接上省流版对比表,也给大家一个对比参考(以下都是指的他们最便宜或次便宜的机型):
| 商家 (VPS 品牌) | 看着眼馋的亮点 | 拔草/劝退的槽点 (吐槽) |
|---|---|---|
| Bandwagon(搬瓦工) | 传家宝级别的神级线路,稳。 | 溢价实在太离谱,动辄上千的价格,去二手交易还容易翻车。(太贵了,肉疼啊!) |
| ByteVirt | 三网直连,还白嫖送快照。 | 内存小到令人发指,稍微跑点服务就得 OOM 报警。 |
| CstoneCloud | 三网直连,电信双程 CN2(尊贵)。 | 入站 30Mbps 挺好,出站带宽只有 10Mbps 的小水管 |
| CubeCloud(魔方云) | 线路好到炸裂(电信CN2+联通A网+移动CMI),带免费快照。 | 除了贵没缺点,最低配接近 1000 块一年,打扰了,告辞。 |
| RFCHost(花卷) | 三网直连,配合中二的 6 折神码我是高手我不需要发工单,性价比直接拉满。 |
真·我是高手我不发工单!用了神码后每个月只能发一次工单,万一遇到点事情就直接干瞪眼了。 |
| SpeedOnline | 便宜大碗,流量多、带宽大。 | 晚高峰电信和联通存在限速,妥妥的“移动快乐机”。 |
| SkyStroll | 上游应该是 ISIF,底子不错。 | 存在薛定谔的限速机制(比如某个时段北京电信限速 5Mbps,广州电信却能跑 100Mbps),买鸡还得看脸? |
| Lisahost(丽萨主机) | 三网直连,稳定性在圈内有口皆碑,带宽够用。 | 邻居成分过于复杂,很多是搞 TikTok 矩阵和做魔法跳板出海的,容易被连坐封 IP。 |
| Ucloud(优刻得) | 大厂背书,价格是真的贱,线路本身也 OK。 | 风控约等于没有!邻居一堆搞发包和扫描作妖的,我之前可是整段整段拉黑过他家 IP 的。 |
| Lightlayer | 无限流量!且能保证晚高峰硬挺跑满。 | 带宽只有 10Mbps,就这网速,给你无限流量你一个月又能跑多少?伪命题。 |
| DogYun(狗云) | 狗云的网络质量确实没得说,晚高峰也能稳定 | 性价比还行,350元/年的价位,带宽给了 25 ~ 50Mbps,流量给得抠抠搜搜(250 ~ 500GB) |
| 硅云 | 便宜,还送 1 年 .top 域名。 | 2M 带宽!绝了,2026 年了居然还有 2M 带宽!放开跑,一个月能用 500GB 算我输。 |
| YT.NET(云途) | 疑似狗云下游的 2025 年新商家。三网直连,极度便宜。最低配才不到200 | 太新了,随时有提桶跑路的风险。但价格是真的香,只要能撑半年就不亏。 |
在对比了多款香港前端机之后,我选择了 YT.NET(云途)作为最终选项,虽然他家过于新(意味着有提桶跑路的可能),而且看网上他家深圳机房有过一次较大的网络故障的问题,但实在是太便宜了,这个价格和配置只要能用个半年的,跑路了也不亏。一个香港三网优化晚高峰还能跑 150 Mbps的线路 1 年才 160 ,还要啥自行车。
以下都是带AFF后的正价,搭配九折优惠券:DIGVPS 可以再打个折上折。
| 型号 | 价格 | 核心 | 内存 | 储存 | 流量 | 带宽 |
|---|---|---|---|---|---|---|
| HK.A | 168 | 1 | 1G | 10G | 500G | 200Mbps |
| HK.B | 300 | 2 | 2G | 20G | 1000G | 500Mbps |
| HK.AMD.D | 180 | 1 | 1G | 10G | 500G | 200Mbps |
| HK.AMD.E | 300 | 2 | 2G | 20G | 1000G | 300Mbps |
表中的路由情况,仅代表我测试的HK.B和HK.AMD.D这两台机器,实测确实都是三网直连的,晚高峰也没出现明显限速和改路由。
| 运营商 | 去程(平时) | 回程 |
|---|---|---|
| 电信V4 | CN2 | CUG(10099) |
| 移动V4 | CMI | CMI |
| 联通V4 | 4837 | CUG(10099) |
| 电信V6 | 163 | 163 |
| 联通V6 | 4837 | 4837 |
| 移动V6 | CMI | CMI |
HK.AMD.D的机器)。回程倒是没改,还是三网直连。经过询问YT.NET的客服,优化云仅承诺尽可能保证优化,负载实在太高时就可能会切到其他非直连的大陆优化路由,如果想确保三网直连路由需要购买香港精品云主机 系列。


原本我的前端机用的是最无脑的端口转发模式。好处是前端纯纯一“工具人”,Nginx 只管在第四层闭着眼睛转流量,配置极其简单。但缺点也很直接:前端啥也没干,遇到攻击直接穿透到后端(前端被打 = 后端被打)。
更要命的是,YT.NET 的流量是双向计费的!走端口模式等于对等流量,一来一回,流量直接翻倍
为了节约流量包,我一咬牙,决定这次把架构改成 Nginx HTTP/HTTPS 反代。想着让前端机把静态内容缓存住,大部分请求直接在前端消化掉,只有必要时候才去请求后端,美滋滋有没有。
结果……这个折腾哦。
这台机器只有 1G 的小内存和 10GB 的硬盘,根本容不下任何有面板的服务。我想装个雷云 WAF 或者宝塔 BT?做梦!刚装完就磁盘和内存双双爆红,内存被吃得渣都不剩,磁盘只剩1G多一点,只好全自己编译安装了。再加上 WordPress 这个动态博客的复杂性,我硬是跟“前端反代+后端自动化”这套机制死磕了快一个月,才算勉强把它调教服帖。正常工作后,内存占用依然不超过60%,磁盘剩余 3GB。
这中间踩了多少坑、掉了多少头发?全是泪(此处省略一万字内心的疯狂吐槽)篇幅有限,我决定单独开一篇文章,详细说下“ 轻WAF+Nginx 前端反代架构”是如何跑通的,敬请期待(右键,新建markdown文档)。
本文 忍痛割爱“负优化”腾讯云!年付不到200元,我换到了这台“真香”的香港前端机 最初发表于 秋风于渭水。
2026-03-05 23:05:52
年前呢,我给博客折腾了一个小功能,年后写了一篇《拒绝自欺欺人!给博客文章加上 AI 创作等级标识 (附完整 PHP+CSS 代码)》。文章发出去之后,评论区收到了不少朋友的反馈,也和好几个写了相关话题的博主进行了一些交流。然后我就想到,有个现象我好奇很久了:在这个 AI 生产力大爆发的时代,为什么有那么多博主,明明文章很多地方都透漏着文章有经过AI辅助写作,甚至整篇文章都是 AI 生成的,却非要“死不承认”?
过年期间无聊的时间,我一改之前只看AI摘要和标题的阅读方式,把所有RSS聚合订阅里的新文章都看了一遍,颇有些唏嘘。
以前关注的一些还不错的博客,现在点开满篇都是那种经典的“AI 营销号味”——逻辑通顺、排版整齐,永远是“首先、其次、综上所述”,“快来学习2026年最新、本文、详细阐述、通过、解决”,但唯独缺少了博主本人的灵魂、个性和折腾时的“人”味。最滑稽的是,经常出现一些,即便那股“ AI 味”已经溢出屏幕了,依然有嘴硬说这是自己逐字敲出来的人。
就像前文评论区里「石樱灯笼」说的那样:“一代人有一代人的垃圾互联网。”
以前是手动抄袭洗稿,后来是脚本农场批量搬运洗稿,现在轮到了 AI 批量生成了。面对这种 AI 时代的“掩耳盗铃”,我想聊聊我为什么决定要在自己的博客里,挂上这个 AI 标识。
从良性思考方向看(咱们不讨论 AI 营销、内容农场的这种最垃圾的情况),很多人死不承认用了 AI,可能是源于一种“AI 羞耻”。他们觉得,用了 AI 辅助,这篇文章就“上不了台面”,显得自己没水平。
这个和很多视频 UP 主或主播,他们极度忌讳被粉丝发现自己有团队一样,哪怕只是简单的有人帮忙,比如被发现稿子是对象帮忙润色的、资料是雇人查的、视频是外包剪辑的,自己那个“全能天才”的人设就崩塌了。
然而现实是:现代创作流程极其复杂,哪有那么多人能单打独斗搞定全流程?合理分配精力、寻求协作,本就是一种能力。写博客也是一样,为了维护一个“我独自熬夜写出万字长文”的虚假精英光环,把 AI 生成的内容全盘复制,这本质上是在满足自己的数字虚荣心。
而另一种时候,一些博主则陷入了一种控制错觉:“标题是我输入的,按钮是我点的,所以这就是我的作品。” 这就像你买了一份预制菜,放进微波炉转了三分钟,端出来后非说自己是米其林大厨。混淆了“指令(Command)”与“创作(Creation)”的边界。或者说的更加现实一点就是,领导觉得是我给他们的课题,字是我签的,所以把团队的功劳全揽自己身上。
AI 应该是一个高水平的陪练,或者一套提升效率的,额,用 AI 自己的话说叫 “副驾驶”。可以用它来突破信息茧房,查漏补缺,检查代码语法错误,或者优化修改文字语病。在这个过程中,哪怕我让 AI 写了 90% 的代码,但只要代码核心的业务逻辑、排错时的思路,依然是我的,那我就依然有自己的独创性在里面的。
但如果我只是给 AI 一个标题和大方向,让它输出最优SEO的字符串排列组合,那我就会从一个创作者,降级成了一个廉价的“信息中转站”。关掉页面后,脑子里依然空空如也。
如果一篇个人博客文章没有了作为“人”的思考和背书,读者们还为什么要来看我的博客?如果只是为了获取干巴巴的信息,大家完全可以直接去和 ChatGPT 或 Gemini 对话,为什么要听我在这里废话?人家 AI 还能多轮对话,实时反应呢。
建立个人博客的初衷,是为了记录真实的折腾过程、生活感悟和技术积累。对于技术博客来说,最珍贵的往往不是最后那个成功的 「Success 截图」和像教科书般的操作步骤,而是“遇到问题 -> 解决问题 -> 记录方案”的过程,是明知不可为而为之的极客趣味。
AI 带有“上帝视角”,它能写出完美的结果,却写不出我凌晨两点,对着终端上满屏的报错敲键盘时的焦虑。这些人的“不完美”和“主观性”,才是个人博客在 AI 时代依然能够存在的基础。
用了就是用了。坦诚地标出 AI 辅助的比例,不仅是对读者的尊重,更是对自我创作边界的一种警醒。敢于在文末挂上“本文代码由 AI 辅助优化”的标签,其实传递的是一种自信:我清楚地知道哪些是我的独立思考,哪些是 AI 工具的产物;是我在驾驭 AI ,而不是被 AI 所支配。
当你大大方方地承认这一点,读者不仅不会觉得你水平不行,反而会觉得你严谨、靠谱且真实嘛。
与其费时费力地先让 AI 生成一堆废话,然后再绞尽脑汁地喂给 AI 各种提示词去“洗稿”、去“AI 味”,想方设法地遮掩自己没动脑子的事实,这种行为真的就像《皇帝的新衣》一样可笑。
与其披着 AI 织造的“皇帝的新衣”在互联网上招摇过市,不如大大方方穿上自己精心挑选和搭配的衣服。虽然你并没有从种棉花、纺纱织布开始制作这件衣服。就像我们在折腾服务器和代码时,不需要从头去造每一个轮子,完全可以借助 AI 写个正则、查个配置、甚至搭个框架写个脚本——但这两者的本质截然不同。这衣服怎么搭、穿去哪、合不合身,体现的都是你作为一个“活人”的真实品味、逻辑与思考。
给博客加上 AI 创作等级标识,不是为了自我标榜,什么数字时代的清流,洁癖啥的,单纯就是为了图个坦荡。
用了就是用了,大方承认自己借助了高效率的工具,把省下来的精力留在真正需要折腾、需要踩坑的地方有何不好。毕竟,从统计数据来看,读者点进我的博客,真正想看的从来不是一份毫无破绽的教程,而是我灰头土脸填完坑后,揉着酸涩的眼睛,骂了一句“终于搞定了”的那个真实瞬间。这也是在这个 AI 时代,我们内容创作者能保留的最后一点“只属于自己的现实”(Personal Reality)吧
PS:前文提到的AI创作标识,因为实在是被只用 CSS 时,说明文字会超出屏幕的问题(不用 JS 实在是无法准确获取浏览器的尺寸和元素的绝对定位)折腾的够呛,现在改成将具体的AI辅助创作声明放在文章末尾,文章开头的 meta 只放 AI 创作等级标识图标了。
本文 大方承认用了AI辅助写作有多难?个人博客的AI羞耻与数字诚信 最初发表于 秋风于渭水。