安全说明
敏感凭据存储
- AppSecret 不落明文:AppSecret 通过 Halo 官方的 Secret 组件(
$formkit: secret)配置,其值保存在独立的Secret资源中,插件的设置项(ConfigMap)只保存该 Secret 的名称,不保存也不返回任何明文。 - 服务端通过
ReactiveExtensionClient按名称读取该Secret,在内存中解析出 AppSecret 后仅用于向微信换取access_token;不写入日志、异常消息、资源状态或任何持久化位置。 - 面向用户的角色模板(发布到微信公众号)不授予
Secret的读取权限,读取仅发生在插件服务端内部。 - 同步任务记录(
WechatSyncTask)只保存文章输入(标题、摘要、正文 HTML、封面地址等)与同步状态,不包含 AppSecret 等任何凭据。
日志与错误信息的脱敏
同步相关的失败原因会同时出现在服务端日志、任务记录(文章列表悬停展示)与 MCP 工具返回中,因此插件对可能携带凭据的文本做了统一脱敏:
- 异常源头脱敏:WebClient 的响应异常默认会把「请求方法 + 完整 URI(含查询串)」写进异常 message,而微信接口的查询串里带着
access_token(获取 token 的请求还带secret);插件在WechatMpClient中把这些异常统一换成脱敏后的消息,因此日志、异常堆栈、任务记录与 MCP 返回里都不会出现凭据(异常类型、errcode与原始 cause 仍然保留); - 落库前再兜一层:
SyncRecord.failed(...)在持久化失败原因前再次脱敏,避免其它来源的文本把凭据写进任务记录; - URL 脱敏:图片 / 附件地址(可能带签名参数)在日志与错误消息中按
sign=***处理,路径与其它的参数保留,便于定位问题; - 正文不入日志:正文 HTML 属用户内容,日志与 MCP 调用日志中只记录长度、不记录内容;
- 刻意不脱敏的字段:
media_id/thumb_media_id是资源标识而非凭据,保留原值以便排查素材失效;appid、文章标题与postName同样保留。
反向代理(Nginx / 1Panel / 网关)自身的访问日志默认会记录含
access_token的完整查询串,需在代理侧另行处理,见代理侧的日志脱敏。