← 中文资源

简体中文版本

构建带反垃圾的联络表单后端

一个完整的联络表单后端:校验、蜜罐与时间启发式、限流、邮件投递与垃圾拒绝测试。

阅读英文原文

项目概述

本项目实现带反垃圾的联络表单后端:校验必填字段与邮箱形态,用隐藏蜜罐与最小提交耗时筛掉明显机器人,按 IP 限流,先落库再通知运营,并对明显机器人返回成功以避免教会对方。整体思路是廉价地先拒绝大多数,再把昂贵的存储与邮件留给可信提交。

架构与关键决策

单一端点串行执行“校验—筛查—限流—落库—通知”,每步可短路,顺序按成本排列使最便宜的拒绝最先发生。提交记录同时保存结果状态,便于用真实数据调优启发式。蜜罐是 CSS 隐藏字段,计时门槛拒绝快于人类输入速度的提交;两者都在服务端判定,因为客户端检查可被轻易绕过。

实施与测试

以假邮件传输提交有效消息,断言既落库又触发通知;填充蜜罐或使用瞬时时间戳时断言二者皆不发生。超出限额断言 429,窗口后断言恢复;并断言收件人为运营方、发件人来自自有域名,避免表单沦为开放中继。

限制与后续

  • 仅有启发式,未接入 CAPTCHA 或信誉服务。
  • 不支持附件,通知目标单一。
  • 依赖代理提供可信客户端地址,否则共享 NAT 下易误伤或可伪造绕过。

代码示例

pnpm init
pnpm add express pg nodemailer
pnpm add -D typescript tsx vitest
contact-form-backend/
├── src/submit.ts
├── src/spam.ts
├── src/notify.ts
├── src/limits.ts
├── tests/contact.test.ts
└── package.json
if (!isEmail(email) || message.length < 10) return res.status(400).json({ error: 'invalid' });
if (body.website || Date.now() - startedAt < 2000) return silentAccept();
if (!allow(ip)) return res.status(429).json({ error: 'rate_limited' });
await db.insert(submission); await notifyOperator(submission);
return res.status(200).json({ ok: true });
pnpm vitest run