联系表单、登录 endpoint 和公开 API 常成为滥用目标:垃圾信息、暴力破解、抓取和自动提交。有效防护需要多层组合,单层不够。
本文介绍在 Astro、Laravel 和 Node.js 应用中保护表单与 endpoint 的分层方法——与本作品集生产环境使用的模式相同。
防护层
- 限流:在时间窗口内按 IP 限制请求。
- 蜜罐:机器人会填、用户看不见的隐藏字段。
- 表单计时:检测快于人类的提交(< 3.5s)。
- 来源检查:生产环境校验 Origin/Referer。
- 模式校验:服务端使用 Zod 或等价方案。
- 消毒:处理或存储前清理输入。
- 可选 CAPTCHA:流量足够时使用 Cloudflare Turnstile。
限流
应用于敏感的公开 endpoint:
- 登录与注册:每 IP 每分钟 5 次。
- 密码重置:每 IP 每小时 3 次。
- 联系表单:每 IP 每分钟 5 次。
- 未认证 API:视用例而定。
单进程可用内存实现;多副本时用 Redis。返回 429 Too Many Requests 和 Retry-After header。
蜜罐
用 CSS 隐藏的字段(display: none 或 position: absolute; left: -9999px)。机器人会自动填写;用户看不到。若有值则静默拒绝(返回 200 但不处理,避免暴露给机器人)。
表单计时
渲染表单时记录时间戳(隐藏字段或 session)。收到 POST 时,若耗时少于 3–4 秒则拒绝。机器人瞬间提交;用户至少需要几秒阅读填写。
服务端校验
每个 API endpoint 使用 Zod(或等价)模式:
- 严格类型:邮箱格式、电话格式、最大长度。
- 明确必填与可选字段。
- 拒绝含意外字段的 payload(strip 或 reject)。
- 客户端通用错误信息;详情仅记在服务端日志。
消毒
- 纯文本字段去除 HTML。
- 规范化空白与控制字符。
- 截断超长字符串。
- 切勿将用户输入未转义地拼进 SQL 或模板。
来源检查
生产环境校验 Origin 或 Referer header 是否匹配站点域名。拒绝未知域名请求。并非万无一失(可伪造),但能过滤基础机器人和跨域脚本。
Cloudflare Turnstile
垃圾流量足以承受额外摩擦时,Cloudflare Turnstile 轻量且注重隐私。用 TURNSTILE_SITE_KEY(公开)和 TURNSTILE_SECRET_KEY(服务端)配置。服务端在处理表单前验证 token。
endpoint 完整流程
- 限流检查 → 超限返回 429。
- 蜜罐检查 → 有值则静默 200。
- 表单计时 → 过快返回 400。
- 来源检查 → 无效来源返回 403。
- Turnstile 验证(若配置)→ 无效返回 400。
- Zod 校验 → 字段错误返回 400。
- 消毒 → 处理。
常见错误
- 只依赖前端校验。
- 只在登录限流,联系表单和 webhook 不限。
- 蜜罐可见或可聚焦的
tabindex(读屏软件会填写)。 - 对蜜罐返回 403(机器人会学习适应)。
- 记录含邮箱电话的完整 payload(日志中的 PII)。
- 三副本各用内存限流(计数器不共享)。