You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何防护ExpressJs的OTP登录API免受恶意刷取攻击?

解决Express OTP发送API的恶意调用问题

针对你遇到的恶意循环调用OTP发送API导致短信额度耗尽的问题,可以通过以下几种方案组合解决:

1. 实现速率限制

用express-rate-limit中间件限制单IP或单手机号的请求频率,避免短时间内大量请求。如果是多服务器部署,需要配合Redis存储限制数据,确保跨实例生效。

单IP限制的示例代码:

const rateLimit = require('express-rate-limit');
// 配置1分钟内最多5次请求
const otpRateLimiter = rateLimit({
  windowMs: 60 * 1000,
  max: 5,
  message: '请求过于频繁,请1分钟后再试',
  standardHeaders: true,
  legacyHeaders: false
});

// 给发送OTP的路由应用限制
app.post('/api/send-otp', otpRateLimiter, (req, res) => {
  // 你的OTP生成与发送逻辑
});

如果要针对手机号做更精准的限制,搭配rate-limit-redis:

const RedisStore = require('rate-limit-redis');
const redis = require('redis');
const client = redis.createClient({ /* Redis配置 */ });

const phoneLimiter = rateLimit({
  store: new RedisStore({ client }),
  windowMs: 10 * 60 * 1000, // 10分钟
  max: 3, // 同一个手机号最多发3次
  keyGenerator: (req) => req.body.phoneNumber, // 用手机号作为限制标识
  message: '该手机号获取OTP过于频繁,请10分钟后再试'
});

app.post('/api/send-otp', phoneLimiter, (req, res) => {
  // OTP发送逻辑
});

2. 强制CAPTCHA验证

在调用OTP发送接口前,要求用户先完成验证码验证(比如图形验证码、reCAPTCHA),前端Angular集成验证码组件,后端校验验证码有效性,挡住批量自动化请求。

后端校验示例:

app.post('/api/send-otp', async (req, res) => {
  const { phoneNumber, captchaToken } = req.body;
  
  // 校验验证码逻辑(以reCAPTCHA为例)
  const captchaValid = await verifyRecaptcha(captchaToken);
  if (!captchaValid) {
    return res.status(400).json({ error: '验证码无效,请重新输入' });
  }

  // 后续OTP发送逻辑
});

3. 校验请求来源

验证请求的Origin或Referer头,只允许你的Angular应用域名发起请求,虽然请求头可以伪造,但能挡住大部分非专业的恶意调用。

示例代码:

const allowedOrigins = ['https://your-angular-domain.com'];

app.post('/api/send-otp', (req, res) => {
  const origin = req.headers.origin;
  if (!allowedOrigins.includes(origin)) {
    return res.status(403).json({ error: '非法请求来源' });
  }

  // OTP发送逻辑
});

4. OTP重复发送冷却机制

设置OTP的有效期(比如5分钟),在有效期内,同一个手机号再次请求时,直接提示已发送,不重复触发短信发送。用Redis存储OTP和过期时间:

const redis = require('redis');
const client = redis.createClient();

app.post('/api/send-otp', async (req, res) => {
  const { phoneNumber } = req.body;
  const existingOtpKey = `otp:${phoneNumber}`;
  
  // 检查是否已有未过期的OTP
  const hasValidOtp = await client.exists(existingOtpKey);
  if (hasValidOtp) {
    const ttl = await client.ttl(existingOtpKey);
    return res.status(400).json({ error: `OTP已发送,请${ttl}秒后再尝试` });
  }

  // 生成并存储OTP,设置5分钟过期
  const otp = Math.floor(100000 + Math.random() * 900000).toString();
  await client.setEx(existingOtpKey, 300, otp);

  // 调用短信服务商接口发送OTP
  await sendSms(phoneNumber, `你的登录验证码是:${otp}`);
  
  res.json({ message: 'OTP已发送' });
});

5. 异常请求监控与拉黑

定期分析请求日志,对短时间内请求量远超正常阈值的IP或手机号,自动拉黑一段时间。可以结合日志工具(如winston)和Redis实现拉黑逻辑:

// 拉黑IP的中间件
const checkBlacklist = async (req, res, next) => {
  const ip = req.ip;
  const isBlacklisted = await client.exists(`blacklist:ip:${ip}`);
  if (isBlacklisted) {
    return res.status(403).json({ error: '你的IP已被临时限制访问' });
  }
  next();
};

app.post('/api/send-otp', checkBlacklist, otpRateLimiter, (req, res) => {
  // OTP发送逻辑
});

内容的提问来源于stack exchange,提问作者Mihrab Miah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 08:35:34