⚠️ 已过期 - 此文档已被 memory/login_design_final.md 替代
请参考最新文档获取准确的登录逻辑说明。
短信发送防护系统文档¶
概述¶
本系统实现了多层次的短信发送防护机制,有效阻止验证码刷取、暴力破解等异常行为。防护组合采用"深度防御"策略,99.9% 的刷验证码脚本都无法绕过。
防护架构¶
防护层级(9层)¶
┌─────────────────────────────────────────────────────────┐
│ 1. 基础参数验证(手机号格式、必填项) │
├─────────────────────────────────────────────────────────┤
│ 2. User-Agent 脚本检测(curl、wget、python等) │
├─────────────────────────────────────────────────────────┤
│ 3. IP/手机号黑名单检查 │
├─────────────────────────────────────────────────────────┤
│ 4. 验证码错误次数限制(5次错误→15分钟锁定) │
├─────────────────────────────────────────────────────────┤
│ 5. 图形验证码校验(Session存储,5分钟过期) │
├─────────────────────────────────────────────────────────┤
│ 6. 一次性请求Token验证(防重放) │
├─────────────────────────────────────────────────────────┤
│ 7. 频率限制检查(IP+Phone分钟级 + Phone小时级) │
├─────────────────────────────────────────────────────────┤
│ 8. 请求记录与监控 │
├─────────────────────────────────────────────────────────┤
│ 9. 短信发送 + 云函数二次限制 │
└─────────────────────────────────────────────────────────┘
核心防护机制¶
1. User-Agent 脚本检测¶
目的:拦截已知的自动化工具和爬虫
黑名单关键词:
curl, wget, python, java, node, go-http-client,
scrapy, selenium, phantomjs, headlesschrome, bot,
spider, crawler, scraper, postman, insomnia
触发条件: - UA为空 → 视为可疑 - UA包含黑名单关键词 → 拒绝
响应:HTTP 429 Too Many Requests
日志:[脚本检测] IP: xxx, UA: xxx, Phone: xxx
2. IP/手机号黑名单¶
目的:快速拦截已识别的恶意来源
黑名单来源: - 验证码错误达到上限的IP(自动加入) - 手动添加的恶意IP/手机号
有效期: - IP黑名单:60分钟 - 手机号黑名单:30分钟
响应:HTTP 429 Too Many Requests
日志:
- [IP黑名单] IP: xxx 被拒绝
- [手机号黑名单] Phone: xxx 被拒绝
3. 验证码错误次数限制¶
目的:防止暴力破解图形验证码
规则: - 每个IP最多5次错误尝试 - 达到上限后锁定15分钟 - 自动将IP加入黑名单
错误计数场景: - 验证码缺失 - 验证码过期 - 验证码不匹配
响应: - 错误时返回剩余尝试次数 - 达到上限返回 HTTP 429
日志:
- [验证码缺失] IP: xxx, Phone: xxx
- [验证码错误] IP: xxx, Phone: xxx, 错误次数: 4
- [验证码锁定] IP: xxx 验证码错误次数已达上限
4. 图形验证码校验¶
目的:人机交互验证,防止自动化脚本
实现: - 服务端生成随机4位数字验证码 - 转换为图片返回给前端 - Session存储验证码和生成时间 - 用户输入后进行校验
有效期:5分钟
特性: - 一次性使用(验证后立即清除) - 验证失败时记录错误次数 - 验证成功时清除该IP的错误计数
端点:
GET /Account/GetCaptcha
5. 一次性请求Token¶
目的:防止请求重放攻击
流程:
1. 前端调用 GenerateSmsToken 端点
POST /Account/GenerateSmsToken
Body: { phone: "13800138000", purpose: "login" }
2. 后端生成32位随机Token,存储在内存缓存
Response: { success: true, token: "abc123..." }
3. 前端在发送短信请求时携带Token
POST /Account/SendSmsCode
Body: { phone: "13800138000", purpose: "login",
captcha: "1234", smsToken: "abc123..." }
4. 后端验证Token,验证成功后立即删除(一次性)
如果Token无效或已过期,拒绝请求
有效期:5分钟
特性: - 每个 Phone+Purpose 组合独立生成 - 验证后自动删除,无法重复使用 - 过期自动清除
端点:
POST /Account/GenerateSmsToken
6. 频率限制¶
目的:防止短时间内大量请求
分钟级限制(IP + Phone): - 阈值:3次/分钟 - 超限返回 HTTP 429 - 建议等待时间:60秒
小时级限制(Phone): - 阈值:10次/小时 - 超限返回 HTTP 429 - 建议等待时间:3600秒
计数方式: - 每次成功发送短信时记录 - 使用内存缓存自动过期
日志:
- [频率限制] IP: xxx, Phone: xxx, Message: 请求过于频繁,请1分钟后再试
7. 云函数二次限制¶
位置:cloudfunctions/sendSMS/index.js
限制规则: - 间隔限制:同一手机号60秒内最多1次 - 小时限制:同一手机号1小时内最多5次 - OpenID限制:同一用户1小时内最多10次
说明:即使绕过Web端防护,云函数仍会进行二次限制
配置参数¶
SmsRateLimitService 中的可配置常量¶
// 防护阈值配置
private const int MAX_REQUESTS_PER_MINUTE = 3; // 每分钟最多3次请求
private const int MAX_REQUESTS_PER_HOUR = 10; // 每小时最多10次请求
private const int MAX_CAPTCHA_ERRORS = 5; // 验证码错误5次后锁定
private const int CAPTCHA_ERROR_LOCKOUT_MINUTES = 15; // 验证码错误锁定15分钟
private const int IP_BLACKLIST_DURATION_MINUTES = 60; // IP黑名单60分钟
private const int PHONE_BLACKLIST_DURATION_MINUTES = 30; // 手机号黑名单30分钟
调整建议: - 生产环境可根据实际情况调整阈值 - 建议不要设置过宽松,否则防护效果下降 - 建议不要设置过严格,否则影响正常用户体验
日志监控¶
日志标记¶
所有防护触发点都有明确的日志标记,便于监控和分析:
| 标记 | 含义 | 严重级别 |
|---|---|---|
[脚本检测] |
检测到脚本UA | Warning |
[IP黑名单] |
IP在黑名单中 | Warning |
[手机号黑名单] |
手机号在黑名单中 | Warning |
[验证码锁定] |
验证码错误达到上限 | Warning |
[验证码缺失] |
验证码为空 | Warning |
[验证码过期] |
验证码超过5分钟 | Warning |
[验证码错误] |
验证码不匹配 | Warning |
[Token缺失] |
请求Token为空 | Warning |
[Token无效] |
Token无效或已过期 | Warning |
[频率限制] |
请求频率超限 | Warning |
[短信发送] |
短信发送请求 | Information |
[短信成功] |
短信发送成功 | Information |
[短信失败] |
短信发送失败 | Warning |
[短信异常] |
短信发送异常 | Error |
日志查看¶
Linux服务器:
# 查看最近100行日志
journalctl -xeu lixiao-web.service -n 100 --no-pager
# 查看特定时间范围的日志
journalctl -xeu lixiao-web.service --since "2024-03-11 10:00:00" --until "2024-03-11 11:00:00"
# 实时监控日志
journalctl -xeu lixiao-web.service -f
Windows开发环境: - 查看 Visual Studio 输出窗口 - 或查看应用日志文件(如配置了文件日志)
前端集成¶
两步流程¶
所有短信发送请求都遵循两步流程:
第一步:生成Token¶
fetch('/Account/GenerateSmsToken', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
phone: "13800138000",
purpose: "login" // login, register, reset
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
smsToken = data.token; // 保存Token
// 继续第二步
}
});
第二步:发送短信¶
fetch('/Account/SendSmsCode', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
phone: "13800138000",
purpose: "login",
captcha: "1234", // 用户输入的图形验证码
smsToken: smsToken // 第一步获得的Token
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 短信发送成功
}
});
已更新的页面¶
Views/Account/Login.cshtml- 登录页面Views/Account/Register.cshtml- 注册页面Views/Account/ForgotPassword.cshtml- 忘记密码页面
异常行为识别¶
典型攻击特征¶
| 攻击类型 | 特征 | 防护机制 |
|---|---|---|
| 脚本自动化 | UA为curl/wget/python等 | UA检测 → 429 |
| 暴力破解验证码 | 短时间内多次错误输入 | 错误次数限制 → 黑名单 |
| 频繁请求 | 同一IP/手机号短时间内多次请求 | 频率限制 → 429 |
| 请求重放 | 重复使用同一请求 | Token一次性验证 |
| 代理/VPN | 来自已知恶意IP | IP黑名单 |
| 批量刷取 | 大量不同手机号请求 | 小时级限制 + 云函数限制 |
监控告警建议¶
建议监控以下指标:
- 429错误率:如果短时间内大量429错误,说明有攻击
- 验证码错误率:如果某个IP的验证码错误率过高,可能是暴力破解
- 频率限制触发:监控频率限制被触发的频率和来源IP
- 黑名单增长:监控黑名单中IP/手机号的增长速度
性能考虑¶
内存占用¶
防护系统使用 IMemoryCache 存储:
- 验证码(每个用户一条,5分钟过期)
- 错误计数(每个IP一条,15分钟过期)
- 频率计数(每个IP+Phone一条,1分钟过期;每个Phone一条,1小时过期)
- Token(每个Phone+Purpose一条,5分钟过期)
- 黑名单(每个IP/Phone一条,30-60分钟过期)
估算: - 正常用户量:1000人/天 - 平均内存占用:< 10MB - 峰值内存占用:< 50MB
响应时间¶
防护检查的性能开销: - 参数验证:< 1ms - UA检测:< 1ms - 黑名单查询:< 1ms - 频率限制检查:< 2ms - Token验证:< 1ms
总计:< 10ms(可忽略不计)
故障排查¶
常见问题¶
Q1: 用户反馈无法获取验证码¶
可能原因: 1. 验证码错误次数已达上限(IP被锁定) 2. 频率限制(请求过于频繁) 3. 手机号在黑名单中 4. 网络问题
排查步骤:
# 查看日志中的错误信息
journalctl -xeu lixiao-web.service | grep "Phone: 13800138000"
# 查看是否有 [验证码锁定] 或 [频率限制] 标记
Q2: 某个IP被误加入黑名单¶
解决方案: 1. 等待黑名单过期(60分钟) 2. 或手动清除缓存(重启应用)
代码中手动清除(如需要):
// 在 SmsRateLimitService 中添加公开方法
public void RemoveIpFromBlacklist(string ip)
{
var key = string.Format(IP_BLACKLIST_KEY, ip);
_cache.Remove(key);
}
Q3: 验证码显示不出来¶
可能原因: 1. 图形验证码生成失败 2. Session配置问题 3. 浏览器缓存
排查步骤:
# 检查 GetCaptcha 端点是否正常
curl http://localhost:5000/Account/GetCaptcha -o captcha.png
# 检查Session是否启用
# 查看 Program.cs 中的 AddSession 配置
安全建议¶
部署前检查清单¶
- 确认
SmsRateLimitService已注册到依赖注入容器 - 确认
IMemoryCache已配置 - 确认所有三个页面(Login/Register/ForgotPassword)都已更新
- 确认前端两步流程已正确实现
- 确认日志级别设置为 Information 或更低
- 确认云函数的二次限制已启用
- 测试各种异常场景(错误验证码、频繁请求等)
生产环境建议¶
- 监控告警:
- 设置429错误率告警
- 设置黑名单增长速度告警
-
设置验证码错误率告警
-
日志收集:
- 使用ELK/Splunk等日志系统收集日志
-
建立日志分析规则,自动识别攻击
-
定期审查:
- 每周审查黑名单中的IP/手机号
- 分析异常流量模式
-
根据实际情况调整防护参数
-
备用方案:
- 配置WAF(Web应用防火墙)进行额外防护
- 配置CDN的DDoS防护
- 准备应急响应流程
相关文件¶
| 文件 | 说明 |
|---|---|
Services/SmsRateLimitService.cs |
防护服务核心实现 |
Controllers/AccountController.cs |
集成防护的控制器 |
Views/Account/Login.cshtml |
登录页面(已更新) |
Views/Account/Register.cshtml |
注册页面(已更新) |
Views/Account/ForgotPassword.cshtml |
忘记密码页面(已更新) |
Program.cs |
依赖注入配置 |
版本历史¶
| 版本 | 日期 | 说明 |
|---|---|---|
| 1.0 | 2024-03-11 | 初始版本,实现9层防护机制 |
联系方式¶
如有问题或建议,请联系技术支持: - 电话:189-5517-3776 - 工作时间:周一至周日 9:00-21:00