管理密钥与机密

本页内容

使用本清单决定哪些值属于运行时环境、哪些生成到数据库、哪些由插件存储。每一节说明轮换如何影响正在运行的站点。

在 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 控制台)环境通过该提供方登录会停止,直到替换
Turnstile 机密你(Cloudflare 控制台)环境评论 CAPTCHA 验证失败
S3 凭证你(存储提供方)运行时环境媒体上传/下载失败,直到替换
插件机密你(管理设置 UI)加密的数据库设置恢复匹配的加密密钥或重新输入值
CLI 凭证emdash login / emdash plugin publish 设备流~/.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 与设置键作为认证数据。格式错误的值会产生面向运维人员的启动消息,需要加密插件设置的操作会 fail closed。无关的站点请求继续工作。

以下命令生成格式正确的值。将其存入运行时环境, 或在部署使用该变量时存为 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 表,因此在请求、部署与 isolate 之间保持稳定。生成是原子的——并发冷启动会收敛到一个值。

预览机密

签署预览 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 上为文件系统)。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(或无前缀别名)
Marketplace 发布(CI)EMDASH_MARKETPLACE_TOKEN
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 不需要访问密钥变量,因为绑定授予运行时访问。见媒体存储。

插件机密

插件以 type: "secret" 声明的设置(邮件提供方的 API 密钥、表单 CAPTCHA 等)在管理 UI 中输入,并加密到 options 表的 plugin:<id>:settings:<key> 下。即使匹配的加密密钥不可用,管理后台也只收到机密是否已设置,以便管理员替换无法读取的凭证。插件代码在其隔离运行时通过 ctx.settings 读取明文。存储在已声明设置模式之外的值,包括任意插件 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 覆盖存储的令牌。
  • Marketplace 令牌 — emdash plugin publish 通过 GitHub 设备流对 EmDash Marketplace 认证,并按 marketplace:<origin> 存储结果 JWT。CI 发布请改设 EMDASH_MARKETPLACE_TOKEN——它优先于存储的凭证。

丢失该文件无害:再次运行 emdash login(或会重新运行设备流的 emdash plugin publish)。

插件注册表 CLI 凭证

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

  • 通过 atproto OAuth 认证。OAuth 会话/状态 blob 位于 ~/.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 认证。

轮换速查

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