一篇真实记录:从发现一个社区 Skill,到安全审计、净化改造、最终跑通自动发布管线的全过程。
起因:让 AI 帮我管博客
我有个老技术博客(lovecto.cn),平时发 ComfyUI、Java 这些东西。写了这么多年,最烦的不是写文章,而是发布这件事本身:写完还得登录后台、复制粘贴、填分类、传封面、设标签……一套下来半小时没了。
所以我想:能不能让 AI 直接管我的博客?我给指令,它出稿、建草稿、我点个头,文章就上去了。
正好我平时在用一个 AI 助手(我管它叫「宇恩」,是我自己配的智能体),也支持挂 Skill。我就去翻社区上有没有现成的「WordPress 管理 Skill」。
结果还真找到了一个 —— SkillsMP 上的一个 wordpress-skill,来自一个有 1300 多 star 的仓库,看起来挺正规。
但我没想到,这一扒,扒出来一个安全大瓜。
第一步:先审计,再使用
这是我和宇恩定下的第一条规矩:别人开源的脚本,先审代码,再用。
宇恩派了个「子代理」(可以理解为一个专门干审查活的独立进程)把那个 Skill 的源码逐段读了一遍。这个脚本叫 wordpress-cli.py,27KB,700 多行,功能挺全:文章、页面、媒体、分类、标签、搜索,六个模块。
审查结果分两半,都很关键。
好消息:技术选型是对的
它用的是 WordPress Application Password + Basic Auth。
这是 WordPress 5.6 以后官方推荐的认证方式,不用装任何插件、不用主账号密码、可以单独吊销。比我原本设想的方案还正。命令的默认行为也很贴心:
post create默认建草稿,不是直接发布post delete默认进回收站,--force才永久删
这俩默认值,简直就是为我那套「AI 出稿 → 我审核 → 发布」的流程量身定做的。
坏消息:他把自家密码挂网上了
脚本开头是这样一段代码:
WP_URL = "https://某人的博客.com"
WP_USER = "xxxx_admin"
WP_APP_PASSWORD = "xxxx xxxx xxxx xxxx xxxx xxxx" # ← 明文,公开可见
这不是占位符的写法。
占位符通常长这样:YOUR_PASSWORD_HERE、xxx、changeme。而这里是一串规整的 24 位分组字符,是 WordPress 应用密码的标准格式 —— 也就是说,它极可能是一组真实可用的凭据,而这段代码,就这么提交在了公开的 GitHub 仓库里。
意味着什么?任何看到这个仓库的人,只要那个站点还开着 REST API,就可能用这对凭据,以管理员身份读写、发布、甚至删除站点上的所有文章。他的 SKILL.md 里,连分类结构都写死了。
我当时心里就一句话:开源不是让你裸奔。
(出于安全考虑,本文已对原作者的站点信息与凭据做了脱敏处理 —— 我不打算成为二次泄漏的一环。如果你在自己的项目里见过类似写法,请立刻轮换那组凭据。)
第二步:净化改造(改动其实很小)
好在,这个脚本虽然工程习惯差,但代码中心化程度不错 —— 所有请求都汇聚到一个 _request() 函数、一个 API_BASE 常量。所以改造点很集中,我做了四件事:
1. 凭据从硬编码 → 环境变量
把顶部那段写死的配置,改成从环境变量读取:
def _require_env(name: str) -> str:
val = os.environ.get(name, "").strip()
if not val:
print(f"❌ 缺少环境变量 {name},请先设置后再运行。")
sys.exit(2)
return val
WP_URL = _require_env("WP_URL").rstrip("/")
WP_USER = _require_env("WP_USER")
WP_APP_PASSWORD = _require_env("WP_APP_PASSWORD")
这样凭据存在加密配置里,不进代码、不进 Git。缺配置时友好报错、退出码 2,不会莫名其妙崩掉。
2. 去掉硬编码的代理
原脚本里写死了:
PROXIES = {
"http": "http://127.0.0.1:7890",
"https": "http://127.0.0.1:7890",
}
这是作者本机的 Clash 代理端口。我的机器上没这个代理,不改的话所有请求会直接报 ProxyError。改成默认不走代理,需要时用 WP_PROXIES 环境变量注入。
3. 修掉重复代码
构造 Basic 认证 token 的逻辑,原脚本写了两遍(媒体上传那里又抄了一遍)。统一收敛到 _headers() 函数。
4. 补上安全约定和文档
重写了 SKILL.md,把标准工作流、安全红线、故障排查都写清楚。核心三条:
- 默认只建草稿,用户不说「发」,绝不发布
- 禁用
--force,删除只进回收站 - 建议用 Author 角色 + 专用应用密码,别用管理员账号
改完做了一轮验证:扫描残留凭据(干净)、语法检查(通过)、缺环境变量行为(友好报错)、传凭据后行为(正常进命令帮助)。全部通过。
第三步:跑通管线
配置认证
我登录 WordPress 后台 → 用户 → 个人资料 → 拉到最底下「应用程序密码」,添加一个叫「宇恩AI助理」的,拿到一串 24 位密码。
三个环境变量一配,跑了个连通性测试:
python3 wordpress-cli.py post list --per-page 3
结果:
[1142] publish 在 ComfyUI 中搭建 Z-Image-Turbo 文生图工作流
[1139] publish API远程访问comfyui
[1133] publish ComfyUI桌面版windows环境搭建
通了。 认证完全通过。
实测五步
然后我把整条流程跑了一遍做验证:
# ① 建草稿(默认状态就是 draft)
python3 wordpress-cli.py post create "测试草稿:..." \
--content-file test-article.html \
--categories 34 --tags 246 --status draft
# → ✅ 文章已创建: [1146] 状态: draft
# ② 确认草稿隔离(只在 draft 列表,前台不可见)
python3 wordpress-cli.py post list --status draft
# → [1146] draft 测试草稿:...
# ③ 用户说「发布」
python3 wordpress-cli.py post update 1146 --status publish
# → ✅ 状态: publish
# 链接: https://www.lovecto.cn/20260920/1146.html
# ④ 验证上线
python3 wordpress-cli.py post get 1146
# → 状态: publish ✅
# ⑤ 删掉测试文(进回收站,不永久删)
python3 wordpress-cli.py post delete 1146
# → ✅ 文章已删除: 1146
# 列表已无残留,草稿列表「暂无文章」
五步全过。 站点回到测试前的状态,一点痕迹没留。
我从中总结的几条经验
1. 开源脚本,先审后用
这不是洁癖。这次要是直接跑,我就等于把自家站点的钥匙,丢进了一个别人写的、可能随时更新的脚本里。审一遍只花几分钟,省掉的是不可逆的风险。
2. 凭据永远不要进代码
不管是开源仓库、私有仓库,还是随手发个截图 —— 凭据一旦离开你的可控范围,就当作已泄漏。 正确做法只有一个:环境变量 / 密钥管理服务。这也是我改造里唯一不能妥协的一条。
3. 好的默认值,比功能多更重要
这个脚本功能不算特别强,但 draft 默认、删除进回收站这两个默认值,直接决定了它能不能用在「AI + 人工审核」的场景里。安全的设计,应该体现在默认行为上,而不是靠使用者记得加参数。
4. AI 自动化,一定要留「人工闸门」
我的流程是:AI 出稿 → 建草稿 → 我审核 → 我点头才发布。AI 可以承担 80% 的活,但最后那一下,得是人来按。尤其在发布这种「发出去就收不回」的操作上。
最后
现在我发博客的流程变成了这样:
我跟 AI 说:「写一篇 ComfyUI 工作流优化的文章,发到我博客。」
它写完、建好草稿、把链接甩给我。
我看一眼,说:「过了。」
上线。
从半小时,变成一句话。
写完这篇,我没打算去点名谁。开源社区里这种疏忽太常见了 —— 不是恶意,只是没意识到代码是可以被任何人读到的。
所以真正想留下的,不是一句嘲讽,而是一个可执行的动作:
去你的 WordPress 后台,把「应用程序密码」里不认识的那一条,删掉。
顺便检查一下,你的任何代码仓库里,有没有
=号右边直接跟着一串密码的地方。
这世上没有「临时先这样」的密码,只有「已经泄漏」的密码。
本文由我口述、AI 助理宇恩整理。真实过程,无夸张成分。
LoveCTO

