Resend邮件轰炸投毒(Reputation Poisoning)解决方案
文章目录
- Resend邮件轰炸投毒(Reputation Poisoning)解决方案
- 测试地址解决不了生产环境滥用
- 项目应该怎么做
- 1. 使用独立发送子域名
- 2. 在调用 Resend 之前限流
- 3. 注册接口加入 Turnstile
- 4. 发送前进行低成本检查
- 5. 接入 Resend Webhook
- 6. 未验证账户不要立即获得完整资源
- 7. 建立熔断机制
- 推荐的最低实现
- 附
- 📌 问题的本质
- 🛡️ 防御方案(多层次)
- 1️⃣ 发送前验证邮箱(最有效)
- 2️⃣ 频率限制(Rate Limiting)
- 3️⃣ 人机验证(阻止自动化攻击)
- 4️⃣ 延迟发送 + 退信自动抑制
- 5️⃣ 使用独立子域名发送验证邮件
- 📊 防御层次总结
- 💡 最低成本方案(适合小项目)
Resend邮件轰炸投毒(Reputation Poisoning)解决方案
Resend邮件轰炸投毒(Reputation Poisoning)是一个真实风险。
Resend 官方甚至直接举了例子:有人 DDoS 注册页面,系统向大量一次性或不存在的地址发送验证邮件,导致大量退信。因此 Resend 建议不要直接使用根域名发信,而应使用专门的邮件子域名,以便出现问题时隔离信誉影响。(Resend)
Resend 要求账户退信率保持在4% 以下,超过后可能暂停发送;垃圾邮件投诉率要求低于0.08%。对于刚上线、邮件量较小的项目,攻击者制造少量退信就可能把比例迅速拉高。(Resend)
测试地址解决不了生产环境滥用
这些地址:
bounced@resend.dev complained@resend.dev suppressed@resend.dev只是让开发者测试退信处理逻辑时,不必随意构造假邮箱。
真实注册页面仍然必须自行防止攻击者输入:
random123456@gmail.com nobody@example-company.com victim@gmail.com前两类可能产生退信;第三类可能真的收到邮件,然后把你标记为垃圾邮件。
项目应该怎么做
1. 使用独立发送子域名
不要直接使用:
verify@your-domain.com建议在 Resend 中单独验证一个邮件子域名,例如:
auth.your-domain.com发件人为:
your-project<verify@auth.your-domain.com>或者:
notifications.your-project.your-domain.comResend 明确建议使用子域名进行“信誉隔离”;即使注册邮件流量被攻击,也可以停用或替换该子域名,而不是直接损害根域名。(Resend)
这不能保证根域名完全不受关联影响,但能显著缩小故障范围。
2. 在调用 Resend 之前限流
不要把 Resend 自带的 API 限流当作反滥用措施。Resend 当前默认限制是整个 Team 每秒 10 个 API 请求,这只是平台容量限制,并不能阻止攻击者消耗你的额度和信誉。(Resend)
注册接口至少需要同时限制:
| 维度 | 建议起始值 |
|---|---|
| 同一邮箱 | 60 秒内最多发送 1 次 |
| 同一邮箱 | 每小时最多 3 次 |
| 同一邮箱 | 每天最多 5 次 |
| 同一 IP | 10 分钟最多注册 5 次 |
| 同一 IP | 每天最多注册 20 次 |
| 全站异常流量 | 达到阈值后启用验证码或暂停发送 |
数据应放在 Redis、Upstash Redis 等服务端存储中,不能只依赖前端按钮倒计时。
3. 注册接口加入 Turnstile
你已经在使用 Cloudflare,适合直接加入Cloudflare Turnstile:
用户提交注册 ↓ 服务端验证 Turnstile token ↓ 检查 IP / 邮箱限流 ↓ 检查邮箱格式和域名 ↓ 创建 pending 用户 ↓ 调用 Resend验证码必须由后端验证。前端隐藏按钮或者 JavaScript 冷却时间都可以被绕过。
4. 发送前进行低成本检查
可以在调用 Resend 之前检查:
邮箱格式是否合法 域名是否存在 域名是否有 MX 记录 是否为明显的一次性邮箱域名 邮箱是否已在本地退信黑名单但要注意:
abc123456@gmail.com即使这个具体账户不存在,gmail.com仍然具有合法 MX 记录。因此MX 检查只能过滤不存在的域名,不能确定具体邮箱账户存在。
第三方邮箱验证服务可以进一步评估邮箱是否可投递,但也不是百分之百准确。Resend 官方也将第三方可投递性验证作为降低退信率的一种措施。(Resend)
5. 接入 Resend Webhook
至少处理:
email.bounced email.complained email.suppressed email.delivered收到永久退信后,在自己的数据库中记录:
emailStatus:"BOUNCED"canSendEmail:falsebounceReason:"recipient_not_found"之后禁止继续向该邮箱发送验证码。
Resend 本身会把发生硬退信或投诉的地址放入 Suppression List,后续发送会被直接抑制,以保护域名信誉;但你的应用仍应同步保存状态,避免继续创建无意义的发送请求。(Resend) Resend Webhook 可以实时通知应用发生退信和投诉。(Resend)
6. 未验证账户不要立即获得完整资源
推荐账户状态:
PENDING_EMAIL_VERIFICATION VERIFIED BOUNCED BLOCKED EXPIRED未验证用户:
- 不能创建正式任务;
- 不能抓取职位;
- 不能反复触发验证邮件;
- 24 小时或数天后清理;
- 邮箱发生硬退信后直接冻结该注册记录。
7. 建立熔断机制
例如监控最近 15~60 分钟:
发送总数 硬退信数量 投诉数量 单一 IP 注册量 单一邮箱域名的异常数量当短时间退信率异常上升时:
停止自动发送 强制所有注册通过 Turnstile 限制高风险 IP 触发管理员告警不能等 Resend 把整个账户暂停后才处理。
推荐的最低实现
对你目前的 项目,最低上线配置应当是:
独立邮件子域名 + Cloudflare Turnstile + Redis 多维限流 + 邮箱格式和 MX 检查 + Resend bounce/complaint webhook + 本地 suppression 状态 + 未验证账户过期清理其中最重要的是Turnstile、应用层限流、独立邮件子域名和 Webhook。仅仅验证邮箱格式,无法抵御这种攻击。
附
恶意用户确实可以通过大量使用不存在的邮箱注册,来"投毒"你的发送域名信誉。让我详细分析一下这个攻击面和防御方案。
📌 问题的本质
这种攻击叫做“邮件轰炸投毒”(Reputation Poisoning):
攻击者 → 用大量虚假邮箱注册你的网站 → 你的系统自动发送验证邮件到这些不存在的地址 → 大量退信(Hard Bounce) → 邮件服务商认为你在发垃圾邮件 → 你的域名信誉下降 → 正常用户的邮件也进垃圾箱或被拒收Resend、SendGrid、SES 等服务都会追踪你的bounce rate(退信率),通常:
- 退信率 > 5%:警告
- 退信率 > 10%:可能暂停你的发送权限
🛡️ 防御方案(多层次)
1️⃣ 发送前验证邮箱(最有效)
在发送验证邮件之前,先检查邮箱是否真实存在:
// 使用邮箱验证 API(如 Hunter.io、ZeroBounce、NeverBounce)import{verifyEmail}from'some-email-validator';app.post('/register',async(req,res)=>{const{email}=req.body;// 第一层:格式校验if(!isValidEmailFormat(email)){returnres.status(400).json({error:'邮箱格式错误'});}// 第二层:检查一次性邮箱(临时邮箱)if(isDisposableEmail(email)){returnres.status(400).json({error:'不支持临时邮箱'});}// 第三层:MX 记录检查(域名是否能收邮件)consthasMX=awaitcheckMXRecord(email.split('@')[1]);if(!hasMX){returnres.status(400).json({error:'该邮箱域名无法接收邮件'});}// 第四层(可选):调用验证 API 检查邮箱是否真实存在constverification=awaitverifyEmail(email);// ZeroBounce / Hunter.ioif(verification.status==='invalid'){returnres.status(400).json({error:'该邮箱地址不存在'});}// 全部通过后,才发送验证邮件awaitsendVerificationEmail(email);});2️⃣ 频率限制(Rate Limiting)
importrateLimitfrom'express-rate-limit';// 限制同一 IP 的注册频率constregisterLimiter=rateLimit({windowMs:60*60*1000,// 1 小时max:5,// 最多 5 次注册message:'注册过于频繁,请稍后再试',});// 限制同一邮箱的发送频率constemailLimiter=rateLimit({windowMs:24*60*60*1000,// 24 小时max:3,// 同一邮箱最多发 3 次message:'验证邮件发送过于频繁',});app.post('/register',registerLimiter,emailLimiter,async(req,res)=>{// ...});3️⃣ 人机验证(阻止自动化攻击)
// 使用 hCaptcha / Cloudflare Turnstile / Google reCAPTCHAimport{verifyCaptcha}from'./captcha';app.post('/register',async(req,res)=>{const{captchaToken}=req.body;constisValid=awaitverifyCaptcha(captchaToken);if(!isValid){returnres.status(400).json({error:'人机验证失败'});}// 继续注册流程...});4️⃣ 延迟发送 + 退信自动抑制
// 使用 Resend 的 webhook 处理退信app.post('/webhooks/resend',async(req,res)=>{const{type,data}=req.body;if(type==='email.bounced'){// 将该邮箱加入抑制列表(suppression list)awaitdb.suppressedEmails.create({email:data.to,reason:'bounced',bouncedAt:newDate(),});// 标记该用户账户为可疑awaitdb.users.update({email:data.to},{status:'suspicious',sendBlocked:true});}if(type==='email.complained'){// 用户举报垃圾邮件,立即加入黑名单awaitdb.suppressedEmails.create({email:data.to,reason:'complained',});}});// 每次发送前检查抑制列表asyncfunctionsafeSendEmail(email,content){constisSuppressed=awaitdb.suppressedEmails.findOne({email});if(isSuppressed){console.log(`跳过已抑制邮箱:${email}`);return;}awaitresend.emails.send({from:'...',to:email,...content});}5️⃣ 使用独立子域名发送验证邮件
主域名: app@yourdomain.com → 发送重要通知(订单、密码重置) 验证子域: verify@auth.yourdomain.com → 发送注册验证邮件这样即使验证邮件被投毒,不会影响主域名的信誉,重要邮件依然能正常送达。
📊 防御层次总结
攻击者提交虚假邮箱 │ ▼ ┌─────────────────────────────┐ │ 1. 人机验证 (Captcha) │ ← 阻止自动化批量注册 ├─────────────────────────────┤ │ 2. 频率限制 (Rate Limit) │ ← 限制单位时间注册量 ├─────────────────────────────┤ │ 3. 格式 + 临时邮箱过滤 │ ← 过滤明显无效邮箱 ├─────────────────────────────┤ │ 4. MX 记录检查 │ ← 过滤不存在域名 ├─────────────────────────────┤ │ 5. 邮箱验证 API │ ← 验证邮箱是否真实存在 ├─────────────────────────────┤ │ 6. 独立子域名发送 │ ← 隔离风险 ├─────────────────────────────┤ │ 7. 退信 Webhook + 抑制列表 │ ← 自动止损 └─────────────────────────────┘💡 最低成本方案(适合小项目)
如果暂时不想接入付费验证 API,至少做到:
- ✅Cloudflare Turnstile(免费人机验证)
- ✅频率限制(每 IP 每小时最多 3-5 次注册)
- ✅一次性邮箱黑名单(disposable email domains 列表 开源免费)
- ✅Resend 退信 Webhook→ 自动加入抑制列表
- ✅MX 记录检查(免费,几行代码搞定)
这 5 项组合起来,就能挡住95% 以上的投毒攻击,而且几乎零成本。
