密钥与密钥管理

本页内容

EmDash 在预览、评论、认证、存储和插件中使用少量密钥。本页是完整清单:每个密钥的来源、存储位置、轮换方法以及丢失后的影响。

概览

密钥来源存储位置丢失影响
EMDASH_ENCRYPTION_KEY运维人员 (emdash secrets generate)仅环境变量 / Worker 密钥加密的插件密钥变得不可恢复(静态加密上线后)
预览密钥自动生成(env 覆盖)options 表 (emdash:preview_secret)已发放的预览链接失效;新链接正常
IP 盐值自动生成(env 覆盖)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 是站点用于静态加密插件密钥的密钥。它由运维人员提供,从不存储在数据库中 — 数据库只包含密文,因此泄露的备份不会暴露密钥。

生成一个并设为环境变量(或 Worker 密钥):

npx emdash secrets generate
# emdash_enc_v1_<43 个 base64url 字符>

# Cloudflare:
wrangler secret put EMDASH_ENCRYPTION_KEY

格式为 emdash_enc_v1_ 后跟 32 字节随机数据的无填充 base64url 编码。密钥在运行时启动时验证;格式错误的值会记录面向运维人员的错误,但不会中断请求路径。

轮换

变量接受逗号分隔的密钥列表。第一个条目是主密钥,用于新的写入;所有条目都会尝试用于解密。每个加密值都标记了 8 字符的密钥指纹(kid,可通过 emdash secrets fingerprint <key> 打印),因此运行时会自动选择正确的密钥。

轮换方法:生成新密钥,添加到列表前面(EMDASH_ENCRYPTION_KEY="新密钥,旧密钥"),重新部署,在现有值重新加密后移除旧密钥。

生成的站点密钥

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

预览密钥

签署预览 URL(HMAC)。存储为 emdash:preview_secret;32 字节随机数据,base64url。

  • 覆盖: 如果需要跨多个进程使用相同密钥或出于审计原因固定密钥,请设置 EMDASH_PREVIEW_SECRET(旧别名:PREVIEW_SECRET)。环境变量始终优先于存储值。
  • 轮换: 删除 emdash:preview_secret 行(或更改 env 变量)并重新部署。影响:之前发放的预览链接停止验证。其他不受影响 — 下次预览请求时会生成新密钥(或从 env 读取)。
  • 丢失时: 无不可恢复的内容。预览链接设计上就是短期的。

参阅预览指南了解预览 URL 的构建和验证方式。

IP 盐值

为评论速率限制中使用的评论者 IP 地址 SHA-256 哈希(评论上的 ip_hash)加盐。存储为 emdash:ip_salt。站点特定,因此哈希在 EmDash 安装之间不可关联。

  • 覆盖: 设置 EMDASH_IP_SALT。为向后兼容,也会查询 EMDASH_AUTH_SECRET / AUTH_SECRET — 历史上从这些值推导盐值的安装保持稳定的哈希。
  • 轮换: 更改 env 变量或删除 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_IDEMDASH_OAUTH_GOOGLE_CLIENT_SECRET(或无前缀别名)
GitHub 登录EMDASH_OAUTH_GITHUB_CLIENT_IDEMDASH_OAUTH_GITHUB_CLIENT_SECRET(或无前缀别名)
Marketplace 发布(CI)EMDASH_MARKETPLACE_TOKEN
Turnstile(评论)EMDASH_TURNSTILE_SECRET_KEY(或 TURNSTILE_SECRET_KEY
S3 兼容存储S3_ACCESS_KEY_IDS3_SECRET_ACCESS_KEYS3_ENDPOINTS3_BUCKETS3_REGION

在 Cloudflare 上使用 wrangler secret put 设置;本地放在 .env 中。通过绑定使用 R2 无需凭证 — 访问由 wrangler.jsonc 中的绑定授权,这是 Workers 上推荐的设置。参阅存储选项

插件密钥

插件用 type: "secret" 声明的设置(邮件提供商 API 密钥、表单 CAPTCHA 等)在管理 UI 中输入并存储在数据库中 — 在 options 表的 plugin:<id>:settings:<key> 下,或在插件的键值存储中。存储的密钥是否返回到管理 UI 取决于插件;编写良好的插件仅返回”值已设置”标志而非密钥本身(内置的表单插件就是这样做的)。

  • 轮换: 在提供商处轮换密钥并将新值粘贴到插件的设置页面。立即生效。
  • 丢失时: 在管理后台重新输入值。无其他依赖。

CLI 凭证

emdash CLI 持有两种凭证,都在 ~/.config/emdash/auth.json(遵循 XDG_CONFIG_HOME)中,以仅所有者权限(0600)创建:

  • 站点令牌emdash login 通过 OAuth 设备流对您的 EmDash 实例进行认证,并将结果令牌按实例 URL 索引存储。emdash logout 将其移除;每次调用时 --tokenEMDASH_TOKEN 覆盖存储的令牌。
  • Marketplace 令牌emdash plugin publish 通过 GitHub 设备流对 EmDash Marketplace 进行认证,并将结果 JWT 以 marketplace:<origin> 索引存储。对于 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_DIDEMDASH_PUBLISHER_HANDLEEMDASH_PUBLISHER_PDS 提供身份;EMDASH_REGISTRY_URL 覆盖注册表主机。CI 的自动 publish 仍需要运行器上 ~/.emdash/oauth/ 中的 OAuth 会话文件 — 环境变量本身不携带 OAuth 会话。
  • 发布访问的轮换或撤销在您的 AT Protocol 帐户(例如应用密码)中进行,而非在 EmDash 中。参阅 Atmosphere auth

轮换快速参考

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