安全说明

敏感凭据存储

  • 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 的完整查询串,需在代理侧另行处理,见代理侧的日志脱敏。