跳转至

⚠️ 已过期 - 此文档已被 memory/login_design_final.md 替代

请参考最新文档获取准确的登录逻辑说明。


短信发送防护系统文档

概述

本系统实现了多层次的短信发送防护机制,有效阻止验证码刷取、暴力破解等异常行为。防护组合采用"深度防御"策略,99.9% 的刷验证码脚本都无法绕过。


防护架构

防护层级(9层)

Text Only
┌─────────────────────────────────────────────────────────┐
│ 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 脚本检测

目的:拦截已知的自动化工具和爬虫

黑名单关键词

Text Only
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的错误计数

端点

Text Only
GET /Account/GetCaptcha


5. 一次性请求Token

目的:防止请求重放攻击

流程

Text Only
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 组合独立生成 - 验证后自动删除,无法重复使用 - 过期自动清除

端点

Text Only
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 中的可配置常量

C#
// 防护阈值配置
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服务器

Bash
# 查看最近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

JavaScript
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
        // 继续第二步
    }
});

第二步:发送短信

JavaScript
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黑名单
批量刷取 大量不同手机号请求 小时级限制 + 云函数限制

监控告警建议

建议监控以下指标:

  1. 429错误率:如果短时间内大量429错误,说明有攻击
  2. 验证码错误率:如果某个IP的验证码错误率过高,可能是暴力破解
  3. 频率限制触发:监控频率限制被触发的频率和来源IP
  4. 黑名单增长:监控黑名单中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. 网络问题

排查步骤

Bash
# 查看日志中的错误信息
journalctl -xeu lixiao-web.service | grep "Phone: 13800138000"

# 查看是否有 [验证码锁定] 或 [频率限制] 标记

Q2: 某个IP被误加入黑名单

解决方案: 1. 等待黑名单过期(60分钟) 2. 或手动清除缓存(重启应用)

代码中手动清除(如需要):

C#
// 在 SmsRateLimitService 中添加公开方法
public void RemoveIpFromBlacklist(string ip)
{
    var key = string.Format(IP_BLACKLIST_KEY, ip);
    _cache.Remove(key);
}

Q3: 验证码显示不出来

可能原因: 1. 图形验证码生成失败 2. Session配置问题 3. 浏览器缓存

排查步骤

Bash
# 检查 GetCaptcha 端点是否正常
curl http://localhost:5000/Account/GetCaptcha -o captcha.png

# 检查Session是否启用
# 查看 Program.cs 中的 AddSession 配置


安全建议

部署前检查清单

  • 确认 SmsRateLimitService 已注册到依赖注入容器
  • 确认 IMemoryCache 已配置
  • 确认所有三个页面(Login/Register/ForgotPassword)都已更新
  • 确认前端两步流程已正确实现
  • 确认日志级别设置为 Information 或更低
  • 确认云函数的二次限制已启用
  • 测试各种异常场景(错误验证码、频繁请求等)

生产环境建议

  1. 监控告警
  2. 设置429错误率告警
  3. 设置黑名单增长速度告警
  4. 设置验证码错误率告警

  5. 日志收集

  6. 使用ELK/Splunk等日志系统收集日志
  7. 建立日志分析规则,自动识别攻击

  8. 定期审查

  9. 每周审查黑名单中的IP/手机号
  10. 分析异常流量模式
  11. 根据实际情况调整防护参数

  12. 备用方案

  13. 配置WAF(Web应用防火墙)进行额外防护
  14. 配置CDN的DDoS防护
  15. 准备应急响应流程

相关文件

文件 说明
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