管理密钥与机密

本页内容

使用此清单来判断哪些值属于运行时环境、哪些由系统生成到数据库中,以及哪些由插件存储。每个小节都说明了轮换会如何影响正在运行的站点。

在 Node.js 上,请将运行时机密放入托管平台的机密管理器,使其在进程启动时进入 process.env。对于 Worker,请使用 wrangler secret put。不要将机密值放入 astro.config.mjs、wrangler.jsonc 或 import.meta.env;Vite 可能会将构建时的值嵌入服务器包中。

概览

机密来源存储位置密钥丢失的影响
EMDASH_ENCRYPTION_KEY运维人员(emdash secrets generate)仅限环境变量 / Worker 机密在恢复匹配的密钥之前,无法读取已加密的插件设置
预览机密自动生成(可用环境变量覆盖)options 表(emdash:preview_secret)已发出的预览链接失效;新链接不受影响
IP 盐自动生成(可用环境变量覆盖)options 表(emdash:ip_salt)过去的评论速率限制连续性被重置
会话与 API 令牌每个会话/令牌各自生成会话存储 / 数据库(仅哈希)无影响——明文从不存储
OAuth 提供方凭证你(Google、GitHub 或 Microsoft Entra 控制台)环境变量通过该提供方登录会停止,直到替换
Turnstile 机密你(Cloudflare 仪表盘)环境变量评论 CAPTCHA 验证失败
S3 凭证你(存储提供方)运行时环境媒体上传/下载失败,直到替换
插件机密你(管理端设置界面)加密的数据库设置恢复匹配的加密密钥,或重新输入该值
CLI 凭证emdash login 设备流程~/.config/emdash/auth.json(权限 0600)再次运行设备流程
注册表 CLI 凭证emdash-plugin atproto OAuth~/.emdash/oauth/、~/.emdash/credentials.json(权限 0600)重新登录;身份保存在你的 PDS 上

加密密钥

EMDASH_ENCRYPTION_KEY 用于加密以 type: "secret" 声明的插件设置。EmDash 使用 AES-GCM,并以插件 ID 和设置键作为认证数据。格式错误的值会产生面向运维人员的启动消息,需要加密插件设置的操作会以关闭方式失败。与之无关的站点请求仍可正常工作。

以下命令会生成格式正确的值。若你的部署使用此变量,请将其存放在运行时环境中,或作为 Worker 机密存放。

npx emdash secrets generate
# emdash_enc_v1_<43 base64url chars>

# Cloudflare:
wrangler secret put EMDASH_ENCRYPTION_KEY

格式为 emdash_enc_v1_ 后跟 32 个随机字节,以无填充的 base64url 表示。该值由运维人员提供,不会存储在数据库中。请将其保存在机密管理器中,并另存一份用于恢复的备份。

要轮换密钥,请在前面添加新值,并在逗号后保留旧值:

EMDASH_ENCRYPTION_KEY=emdash_enc_v1_<new-key>,emdash_enc_v1_<old-key>

EmDash 使用第一个密钥加密新值和重新保存的值,并根据已存储的 kid 指纹选择较旧的密钥来读取。在移除旧密钥之前,请重新保存每个插件机密,然后在仅包含新密钥的部署中验证这些集成。EmDash 目前不会报告哪些密钥 ID 仍在使用,因此请为你重新保存的凭证维护一份清单,并在每个集成都通过验证之前不要移除旧密钥。

生成的站点机密

有两个机密会在首次使用时自动生成,并持久化在 options 表中,因此在请求、部署和隔离环境之间保持稳定。生成是原子性的——并发的冷启动会收敛到同一个值。

预览机密

用于对预览 URL 签名(HMAC)。存储为 emdash:preview_secret;32 个随机字节,base64url 编码。

  • 覆盖: 如果需要在多个进程间使用同一机密,或出于审计原因想要固定它,请设置 EMDASH_PREVIEW_SECRET(旧版别名:PREVIEW_SECRET)。环境变量始终优先于存储的值。
  • 轮换: 删除 emdash:preview_secret 行(或更改环境变量)并重新部署。影响:先前签发的预览链接不再通过验证。其他一切不受影响——下一次预览请求时会生成新的机密(或从环境变量读取)。
  • 如果丢失: 没有任何无法恢复的内容。预览链接按设计是短期有效的。

有关预览 URL 如何构建和验证,请参阅预览指南。

IP 盐

为评论者 IP 地址的 SHA-256 哈希(评论上的 ip_hash)加盐,用于评论速率限制。存储为 emdash:ip_salt。它因站点而异,因此各 EmDash 安装之间的哈希无法相互关联。

  • 覆盖: 设置 EMDASH_IP_SALT。为保持向后兼容,也会参考 EMDASH_AUTH_SECRET / AUTH_SECRET——过去由它们派生盐的安装会保持哈希稳定。
  • 轮换: 更改环境变量或删除 emdash:ip_salt 行,然后重新部署。影响:新的评论提交会哈希为不同的值,因此所有人的速率限制计数都会重新开始。现有评论及其已存储的哈希不受影响。
  • 如果丢失: 不会丢失数据。只是速率限制的连续性被重置。

会话与 API 令牌

  • 会话使用 Astro 的会话存储(Cloudflare 上为 Workers KV,Node 上为文件系统)。未配置存储的适配器(如 Vercel)需要在 astro.config.mjs 中配置会话驱动;若没有,登录会失败,并给出说明需要会话驱动的错误。Cookie 携带的是不透明的会话 ID;没有需要管理的签名机密。退出登录可结束会话,或清空会话存储(例如 KV 命名空间)以强制所有人重新登录。
  • API 令牌(前缀为 ec_pat_、ec_oat_、ec_ort_)是不透明的 256 位随机值;只存储其 SHA-256 哈希。明文仅在创建时显示一次。请通过在管理端撤销并重新创建来轮换。
  • 邀请、魔法链接和恢复令牌用途单一,以 SHA-256 哈希的形式存储在 auth_tokens 中,并有时间限制(邀请 7 天,魔法链接 15 分钟)。

无需主动备份或轮换任何内容:数据库泄露只会暴露哈希,且每个令牌都可以在管理端撤销或重新签发。

用户提供的服务凭证

外部服务的凭证从环境变量读取,绝不会写入数据库。请在提供方处轮换,更新变量,然后重新部署。

服务变量
Google 登录EMDASH_OAUTH_GOOGLE_CLIENT_ID、EMDASH_OAUTH_GOOGLE_CLIENT_SECRET(或无前缀别名)
GitHub 登录EMDASH_OAUTH_GITHUB_CLIENT_ID、EMDASH_OAUTH_GITHUB_CLIENT_SECRET(或无前缀别名)
Microsoft 登录EMDASH_OAUTH_MICROSOFT_CLIENT_ID、EMDASH_OAUTH_MICROSOFT_CLIENT_SECRET、EMDASH_OAUTH_MICROSOFT_TENANT_ID(或无前缀别名)
Turnstile(评论)EMDASH_TURNSTILE_SECRET_KEY(或 TURNSTILE_SECRET_KEY)
兼容 S3 的存储S3_ACCESS_KEY_ID、S3_SECRET_ACCESS_KEY、S3_ENDPOINT、S3_BUCKET、S3_REGION

在 Cloudflare 上,请使用 wrangler secret put 设置这些变量;本地开发时,请将它们放入 .env。Wrangler 读取 .dev.vars 或 .env 其中之一,而不是同时读取两者,且存在 .dev.vars 时以其为准。通过绑定使用 R2 不需要访问密钥变量,因为绑定会授予运行时访问权限。参见媒体存储。

在 Node 上,EmDash 会在请求到达时从服务器的进程环境读取 Turnstile 密钥,而 astro dev 和构建后的服务器都不会将 .env 加载到其中。请在主机环境中设置该密钥,在运行 astro dev 的 shell 中导出它,或使用 node --env-file=.env ./dist/server/entry.mjs 启动构建后的服务器。

插件机密

插件以 type: "secret" 声明的设置(电子邮件提供商的 API 密钥、表单验证码等)在管理端界面中输入,并加密存储在 options 表的 plugin:<id>:settings:<key> 下。即使匹配的加密密钥不可用,管理端也只会收到该机密是否已设置,因此管理员可以替换无法读取的凭证。插件代码在其隔离的运行时内通过 ctx.settings 读取明文。存储在已声明的设置 schema 之外的值,包括任意插件 KV 和状态条目,不使用此加密路径。

  • 凭证轮换: 在提供方处轮换凭证,并将新值粘贴到插件的设置页面。保存时会写入新的加密信封。
  • 明文迁移: 由早期 EmDash 版本存储的机密仍可读取。请重新保存每个值以对其加密。
  • 如果加密密钥丢失: 从单独的密钥备份中恢复匹配的 EMDASH_ENCRYPTION_KEY。如果没有任何副本,请在提供方处替换每个受影响的凭证,并在配置新的加密密钥后输入替换值。

CLI 凭证

emdash CLI 将站点令牌存储在 ~/.config/emdash/auth.json(遵循 XDG_CONFIG_HOME)中,并设置为仅所有者可访问的权限(0600)。emdash login 通过 OAuth 设备流程向你的 EmDash 实例认证,并以实例 URL 为键存储令牌。emdash logout 会将其删除;每次调用时,--token 或 EMDASH_TOKEN 会覆盖已存储的令牌。

丢失该文件无妨:再次运行 emdash login 即可。

插件注册表 CLI 凭证

独立的 emdash-plugin CLI(包 @emdash-cms/plugin-cli)面向实验性的 AT Protocol 注册表。在那里发布与你的 AT Protocol 身份(你的发布者 DID)绑定——站点本身不持有任何发布凭证,安装时会根据归属于该 DID 的发布记录中的校验和来验证产物。

  • 它通过 atproto OAuth 认证。OAuth 会话/状态数据位于 ~/.emdash/oauth/,发布者身份(DID、handle、PDS)缓存在 ~/.emdash/credentials.json 中;两者都以仅所有者可访问的权限写入。
  • 在 CI 中,通过 EMDASH_PUBLISHER_DID、EMDASH_PUBLISHER_HANDLE 和 EMDASH_PUBLISHER_PDS 提供身份;EMDASH_REGISTRY_URL 会覆盖注册表主机。在 CI 中自动执行 publish 仍需要运行器上 ~/.emdash/oauth/ 中的 OAuth 会话文件——仅靠环境变量并不携带 OAuth 会话。
  • 轮换或撤销发布访问权限发生在你的 AT Protocol 账户处(例如应用密码),而不是 EmDash 中。参见 Atmosphere auth。

轮换速查

我想要……这样做
轮换插件设置的加密在前面添加新密钥,重新保存插件机密,然后移除旧密钥
使所有预览链接失效删除 emdash:preview_secret 行(或更改环境变量覆盖),重新部署
重置评论速率限制的哈希更改 EMDASH_IP_SALT(或删除 emdash:ip_salt 选项行),重新部署
撤销已泄露的 API 令牌Admin → Users → API tokens → 撤销,然后创建替代令牌
终止所有会话清空会话存储(Workers KV 命名空间 / 会话目录)
替换提供方凭证在提供方处轮换,更新环境变量,重新部署
替换插件 API 密钥在提供方处轮换,在插件的管理端设置中重新输入