MuseMVP 文档

安全基线

启动环境校验、安全响应头、错误边界与 API 错误处理的默认防护。

MuseMVP 内置了一组开箱即用的安全基线:服务启动时校验环境变量、全站下发安全响应头、分层错误边界兜底、API 统一错误处理,以及针对计费 webhook 与邮件链路的加固。这些防护默认启用,无需额外配置。

设计原则

生产环境的配置错误应在启动时快速失败(fail fast),而不是在请求中途暴露为难以排查的运行时错误;非致命问题降级为警告日志,不阻塞部署。

启动环境校验

服务端环境变量由 src/lib/env.ts 中的 Zod schema 校验,经 src/instrumentation.tsregister() 在服务启动时调用一次。

  • 致命错误:生产运行时缺少 BETTER_AUTH_SECRET 会直接抛错终止启动。next build 与本地 dev 不受影响,仅记录错误日志。
  • 非致命警告:以下配置问题只输出警告日志,不阻塞启动。
场景警告条件影响
计费网关已配置网关 API Key 但缺少对应 webhook secret生产环境 webhook 会被拒绝
数据库连接DATABASE_CONNECTION_STRATEGY=database_url_first 但未设置 DATABASE_URL数据库连接失败
邮件服务MAIL_PROVIDER=resend 但未设置 RESEND_API_KEY事务邮件发送失败

安全响应头

next.config.mjsheaders() 对全部路由(/:path*)下发以下安全响应头:

响应头作用
Strict-Transport-Securitymax-age=63072000; includeSubDomains; preload强制 HTTPS
X-Frame-OptionsSAMEORIGIN防止跨站 iframe 嵌入(点击劫持)
X-Content-Type-Optionsnosniff禁止 MIME 类型嗅探
Referrer-Policystrict-origin-when-cross-origin限制 Referrer 泄露
Permissions-Policycamera=(), microphone=(), geolocation=(), browsing-topics=()默认禁用敏感浏览器能力

关于 CSP

Content-Security-Policy 暂未包含在内:Next.js 内联脚本需要按请求生成 nonce,且需为第三方脚本(统计、Turnstile、Google One Tap)逐一放行,计划作为独立事项跟进。


错误边界

App Router 采用分层错误边界,未捕获异常不会导致白屏:

全局兜底

src/app/global-error.tsx — 最后一道防线,替换根 layout 自行渲染 html/body,使用内联样式与非本地化文案。

落地页边界

src/app/(landing-page)/[locale]/error.tsx — 捕获营销页段内异常,渲染共享回退组件。

应用页边界

src/app/(saas-page)/app/error.tsx — 捕获 SaaS 应用段内异常,渲染共享回退组件。

两个分段边界共享 ErrorState 回退组件,提供重试回首页两个操作,文案在 i18n 的 errors 命名空间中维护(en/zh)。


API 错误处理

Hono API(src/backend/api/app.ts)在入口层统一处理错误与请求体大小:

统一错误结构app.onError 捕获所有逃逸出 handler 的异常,记录日志后返回统一 JSON 错误结构({ ok: false, error: "Internal Server Error" }),不向客户端泄露内部细节。

HTTPException 透传:路由主动抛出的 HTTPException 保留其自身响应,不被覆盖。

请求体限制hono/body-limit 限制请求体最大 1MB,超限返回 413。文件上传走 presigned URL 直传对象存储,不受此限制影响。


计费与邮件加固

  • Webhook 签名强制校验:生产环境下 Creem 与 Stripe 的 webhook 强制要求配置 webhook secret,缺失时直接抛错拒绝处理(与 Dodo 行为一致)。
  • Newsletter 退订 token:退订链接 token 采用 HMAC-SHA256 签名并使用常量时间比较验证,token 不可伪造,同时修复了此前的反射型 XSS 问题。
  • Auth 邮件真实报错:OTP、magic link、密码重置邮件发送失败时向客户端返回真实错误,而不是静默显示成功导致用户永远收不到邮件。

相关文档