网站安全
用新手能理解的方式,判断什么时候需要 Cloudflare、限流、人机验证、上传限制和密钥保护
为什么要关心安全
早期产品的安全问题通常不是电影里的“黑客入侵”。更常见的是:你的短信验证码被人疯狂调用,AI 额度一夜用完,反馈表单塞满垃圾内容,上传入口被当成免费网盘,或者支付成功通知被伪造。
这些问题的共同点是:攻击者不一定要真的“攻破系统”,只要能反复调用某个入口,就可能让你花钱、让服务变慢,或者让正常用户没法使用。
安全不放进“部署与运维”下面。部署与运维关注上线、域名、日志、健康检查和统计;安全会横跨登录、支付、AI、上传、权限和管理后台,所以作为独立章节保留。上线检查会从部署指南链接到这里,但具体安全策略在本章节维护。
先按这几个问题判断
| 你遇到的问题 | 优先措施 | 说明 |
|---|---|---|
| 网站请求突然暴涨,页面变慢 | Cloudflare 代理、WAF、边缘限流 | 先在访问到达服务器前挡掉明显异常流量 |
| 短信验证码、邮件验证码被刷 | 接口限流,必要时开启人机验证 | 这是最容易直接产生账单的入口 |
| 登录失败次数异常增多 | 登录限流、2FA、人机验证 | 这是常见的撞库和批量试密码 |
| AI 生成、搜索、上传被频繁调用 | 单独给这些高成本动作限流 | 高成本动作不要只靠全站统一限制 |
| 上传文件越来越多、越来越大 | 上传大小、类型、次数限制 | 防止存储费用失控,也避免违规文件 |
| 担心 API key 泄露 | 密钥只放环境变量,提交前做检查 | 泄露后要第一时间轮换,而不是只删代码 |
安全措施不是越多越好。我的建议是:默认先把 Cloudflare 代理、基础限流、上传边界和密钥管理做好;Turnstile 这类会影响体验的验证,只放在高风险动作或攻击期使用。
安全头和 CSP
01MVP 模板默认设置了一组浏览器安全头,包括 Content-Security-Policy、X-Frame-Options、X-Content-Type-Options、Referrer-Policy 和 Permissions-Policy。
CSP 的作用不是让网站“跑得起来”,而是限制浏览器可以从哪里加载脚本、图片、字体、接口请求和表单提交。MDN 对 Content-Security-Policy 的解释是:它允许网站控制当前页面能加载哪些资源,并帮助防御跨站脚本攻击。Next.js 官方文档也把 CSP 作为防护 XSS、clickjacking 和代码注入攻击的手段。
对初创小团队来说,CSP 更适合在产品域名、第三方统计、验证码、支付、客服、错误上报这些来源稳定后再开启。它的收益是真实的,但维护成本也是真实的:只要忘记把新域名加入 allowlist,浏览器就可能直接拦掉登录、注册、统计、验证码或错误上报请求,用户看到的通常只是 Failed to fetch。
你只需要记住两点:
- 默认安全头不要删。它们是浏览器层面的基础防护。
- 接入新的第三方脚本时,比如 GA、Umami、支付组件、客服插件或 Sentry/PostHog,如果浏览器控制台出现 CSP 报错,把可信域名加进
products/01mvp/apps/web/security-headers.mjs的 allowlist。
模板的 CSP 源配置在 products/01mvp/apps/web/security-headers.mjs。这里会生成 Cloudflare 静态部署用的 products/01mvp/apps/web/public/_headers,也会被运行时安全头复用。改完 allowlist 后运行:
vpr @01mvp/web#security:headers不要直接手改 public/_headers,否则下次构建或安全头生成脚本会把它覆盖。
Cloudflare Web Analytics 是一个典型例子。Cloudflare 官方文档说明,Web Analytics beacon 会从 https://static.cloudflareinsights.com/beacon.min.js 加载;如果网站设置了 CSP,就可能需要更新 CSP 才能加载这个脚本。所以 01MVP 模板默认在 script-src 里放行 https://static.cloudflareinsights.com,否则小橙云自动注入成功了,浏览器也可能把脚本拦掉。
Cloudflare Turnstile 也是模板内置的可选能力。Cloudflare Turnstile 的 CSP 文档要求放行 https://challenges.cloudflare.com 的脚本和 iframe,所以模板默认把它放进 script-src、frame-src 和 connect-src。
GitHub、Google、微信登录不需要因为 OAuth 跳转额外加入 CSP allowlist。它们通常是浏览器顶层跳转到第三方授权页,不是从你的页面加载第三方脚本或 iframe。头像图片也不需要额外加域名,因为模板的 img-src 已允许 HTTPS 图片。
服务端请求也不需要加入浏览器 CSP。比如后端在 Hono、oRPC procedure、server-only module 或后台任务里请求 OpenAI-compatible API、短信服务、支付服务、对象存储,这些请求由服务器发出,不经过用户浏览器的 CSP。只有浏览器页面自己发出的请求才会被 connect-src 限制,比如 analytics beacon、Sentry browser SDK 上报、PostHog 浏览器采集、前端直接 fetch() 第三方接口。
排查 CSP 报错时,看浏览器 Console 里的三件事:被拦的是 script-src、connect-src 还是 frame-src;被拦的完整域名是什么;这个域名是否来自你主动接入的可信服务。确认后再加 allowlist。
如果开启 CSP 后,第三方统计、验证码、客服脚本或登录请求一直被拦住,可以先关闭 CSP 排查。关闭需要同时处理静态头和运行时头:
products/01mvp/apps/web/security-headers.mjs:从BASE_SECURITY_HEADERS中移除["Content-Security-Policy", CONTENT_SECURITY_POLICY],然后运行vpr @01mvp/web#security:headers重新生成public/_headers。products/01mvp/apps/web/src/server/security-headers.ts:从getSecurityHeaders()返回值中移除Content-Security-Policy这一项。
排查完后,再按 Console 里被拦的可信域名补 allowlist,然后决定是否重新打开 CSP。
常见风险
常见风险
短信被刷、AI 额度被刷、垃圾表单、上传滥用、支付通知伪造这些真实场景。
Cloudflare 防护
小橙云、DDoS、WAF、边缘限流、Bot Fight Mode 和 Under Attack 的使用边界。
接口限流
为什么验证码、AI、上传、搜索这些入口要单独限制调用次数。
人机验证
Local PoW 与 Cloudflare Turnstile 的选择,什么时候开启,什么时候不要开启。
上传限制
文件大小、类型、次数和存储成本的控制方式。
密钥保护
API key、短信密钥、AI key、支付密钥泄露后会发生什么,以及怎么处理。
安全能力分层
| 层级 | 保护什么 | 典型措施 |
|---|---|---|
| 访问入口 | 网站别被流量打垮 | Cloudflare 代理、WAF、边缘限流 |
| 付费动作 | 短信、AI、邮件、搜索、上传别被刷 | 应用限流、登录态、配额 |
| 人机判断 | 批量脚本不要轻易通过 | Local PoW、Cloudflare Turnstile |
| 数据和文件 | 文件别无限上传,密钥别泄露 | 上传限制、环境变量、安全检查 |
| 账号权限 | 用户和管理员别越权 | 路由保护、权限模型、2FA、管理员审计 |
账号和权限
下面这些页面更偏开发和后台管理,但仍然属于安全体系:
- 路由保护:哪些页面必须登录、哪些页面只有管理员能看。
- 权限模型:用角色和权限判断用户能做什么。
- 双因素认证:给管理员和高风险账号加第二道验证。
- 管理员系统:管理员角色、权限和用户管理。
- 跨子域名登录:多个子域共享登录态时需要注意的配置。
相关资源
这篇文档有问题?