// docs / baas security / clerk hardening
Clerk 安全清单:20 项
Clerk 为你的应用处理身份验证、会话和组织 — 这意味着配置错误的 Clerk 集成就是身份验证绕过、会话固定向量或组织泄漏路径。本清单是涵盖密钥、会话配置、webhook、组织、JWT 模板和持续监控的 20 项审计。AI 编码工具用合理默认值快速配置 Clerk;本列表捕获它们留下的项目。
有关身份验证层配置错误是 AI 工具弱点的背景,请参阅 AI 编码工具为何留下安全漏洞。有关 Auth0 上的并行清单,请参阅 Auth0 安全清单。
环境密钥和源允许列表
Clerk 为每个项目发行两种不同的密钥。混淆或泄漏它们是第一个失败模式。
- 在浏览器中使用可发布密钥 (生产中为
pk_live_*,开发中为pk_test_*);仅在服务器上使用秘密密钥 (sk_live_*/sk_test_*)。可发布密钥在NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY中是安全的;秘密密钥绝不能带有公共 env 前缀,绝不能出现在客户端组件中。 - 验证生产应用使用
pk_live_*而非pk_test_*。测试实例允许未验证的电子邮件地址和已禁用的 MFA — 将测试模式发布到生产是一种身份验证绕过。 - 在 Clerk 仪表板中配置允许的源。Settings → Domains → Allowed origins 必须准确列出你的生产域名。空或通配符源列表让攻击者能够创建与你的后端通信的伪造 Clerk 前端。
- 在任何离职或疑似泄漏时轮换秘密密钥。仪表板 → API Keys → Reset。旧密钥失效;在轮换前用新值重新部署服务端代码。
会话配置
会话过期和闲置超时是被盗会话变成 10 分钟事件还是 30 天事件的差别。
- 对处理敏感数据的 SaaS 应用,将会话闲置超时设置为 30 分钟或更少。仪表板 → Sessions → Inactivity timeout。银行级应用应使用 5-10 分钟;标准 SaaS 30-60 分钟;消费者应用 1-7 天。默认为 7 天。
- 在密码更改、电子邮件更改和 MFA 注册时启用会话撤销。仪表板 → Sessions → Revoke on。这些是用户发起的安全事件;其他设备上的现有会话应被终止。
- 在每条受保护路由上验证服务端会话,而不仅是在登录时。在 Next.js 中:服务器组件 / API 路由中的
const { userId } = await auth();从 cookie 读取 JWT 并验证它。绝不信任仅 cookie 的检查。 - 在会话 cookie 上设置
SameSite=Lax(默认) 或Strict。在 DevTools → Application → Cookies 中验证。SameSite=None是 CSRF 向量 — 除非你明确配置了跨域身份验证设置,否则绝不使用。
Webhook 验证
Clerk webhook 在用户生命周期事件 (创建、更新、删除、session.ended) 上触发。它们是你数据库的同步机制 — 而伪造的 webhook 是一种数据库写入原语。
- 对每个 webhook 验证 Svix 签名。Clerk webhook 由 Svix 签名。使用
new Webhook(secret).verify(body, headers)。验证失败时用401拒绝。 - 将 webhook 密钥存储在环境变量中,绝不在代码中。密钥在每次仪表板重新生成时轮换 — 你的部署必须从 env 读取它,而非常量。
- 每个处理程序都要幂等。Webhook 投递可能重复。在
webhook_events表中使用svix-id标头作为主键来去重。在同一事务中包装状态变更和幂等插入。 - 在
user.deleted上,24 小时内硬删除或匿名化 PII。GDPR / CCPA 要求这样做。审计删除路径:哪些表持有此用户的数据?在可能的地方使用 FK ON DELETE CASCADE。
组织和权限
如果你使用 Clerk Organizations,组织边界就是你的租户隔离。每个服务端查询都必须按它过滤。
- 在每条 API 路由上,从
auth()读取userId和orgId并按两者过滤数据库查询。WHERE org_id = $orgId AND user_id = $userId。绝不信任来自请求正文的org_id。 - <strong>Use Clerk role checks for privileged operations, not boolean checks against the user object.</strong> <code>has({ role: 'org:admin' })</code> reads the role from the verified JWT. A user can spoof a boolean on a stale client object; they cannot spoof a JWT claim.
- 用两个真实组织账户测试跨组织隔离。创建组织 A,填充数据,在另一浏览器中登录组织 B,尝试通过 API 读取组织 A 的数据。响应必须是
403或404。
JWT 模板和外部集成
JWT 模板将 Clerk 身份推送到 Supabase、Firebase 和其他下游服务。配置错误的模板过度共享声明或暴露你未打算暴露的数据。
- 对每个 JWT 模板,列出每个声明并确认其必要性。仪表板 → JWT Templates。向 Supabase 发送
email和phone的模板会向浏览器中读取 JWT 的任何人暴露 PII。 - 对用于客户端下游调用的 JWT 模板设置短过期时间。下游 API 请求 60 秒是标准。长寿命 JWT 会被盗并重放。
- 在接收端验证受众 (
aud) 声明。Supabase、Firebase 等应检查aud是否与预期服务标识符匹配。没有这个,为服务 A 发行的 JWT 可以认证到服务 B。
运营监控
身份验证是你拥有的最高信号日志源。监视它。
- 对每 IP / 每账户的登录失败激增告警。正常失败率的 50 倍是凭据填充攻击。Clerk 将这些事件发出到 webhook;将它们路由到你的 SIEM。
- 季度审查会话和实例设置漂移。随着 Clerk 更新,默认值会变;「旧配置」会随时间悄悄变错。将仪表板 JSON 导出与你上一个已知良好副本做差异比较。
后续步骤
对你的生产 URL 运行 FixVibe 扫描 — baas.clerk-auth0 检查会标记 Clerk 可发布密钥、生产中的测试密钥和打包的秘密密钥。有关 Auth0 上的等效清单,请参阅 Auth0 安全清单。对于 BaaS 提供商的总览,请阅读 BaaS 配置错误扫描器。
