当前位置: 首页 > news >正文

防止手机验证码被刷:React + TypeScript 与 Node.js + Express 的全面防御策略

防止手机验证码被刷是开发中常见的安全问题,尤其是在涉及用户注册、登录或敏感操作时。攻击者可能会通过自动化脚本频繁请求验证码,导致短信轰炸或资源浪费。以下是如何在 React + TypeScript 前端和 Node.js + Express 后端中防止验证码被刷的深度分析,并结合代码实现。


1. 常见的攻击场景

  1. 短信轰炸

    • 攻击者通过脚本频繁请求验证码,导致用户手机收到大量短信。
  2. 验证码滥用

    • 攻击者通过暴力破解或重放攻击,尝试破解验证码。
  3. 资源耗尽

    • 频繁请求验证码可能导致服务器资源耗尽,影响正常用户。

2. 防御措施

2.1 前端防御措施

  1. 增加图形验证码

    • 在发送短信验证码之前,要求用户输入图形验证码,防止自动化脚本。
  2. 请求频率限制

    • 在前端限制用户点击“发送验证码”按钮的频率(如 60 秒内只能点击一次)。
  3. IP 和设备指纹识别

    • 记录用户 IP 和设备信息,用于后端分析和限制。

2.2 后端防御措施

  1. 请求频率限制

    • 使用 Redis 或内存缓存记录每个手机号或 IP 的请求频率,限制单位时间内的请求次数。
  2. 验证码有效期

    • 设置验证码的较短有效期(如 5 分钟),减少被滥用的风险。
  3. 验证码使用次数限制

    • 每个验证码只能使用一次,使用后立即失效。
  4. 黑名单机制

    • 将频繁请求验证码的 IP 或手机号加入黑名单,暂时禁止其请求。
  5. 日志监控

    • 记录验证码请求日志,监控异常行为。

3. 前端实现(React + TypeScript)

3.1 增加图形验证码

在发送短信验证码之前,要求用户输入图形验证码。

import React, { useState } from 'react';
import axios from 'axios';

const SmsVerification: React.FC = () => {
  const [phone, setPhone] = useState('');
  const [captcha, setCaptcha] = useState('');
  const [isLoading, setIsLoading] = useState(false);

  const handleSendCode = async () => {
    if (!captcha) {
      alert('请输入图形验证码');
      return;
    }

    setIsLoading(true);
    try {
      await axios.post('/api/send-sms', { phone, captcha });
      alert('验证码已发送');
    } catch (error) {
      alert('发送验证码失败');
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <div>
      <input
        type="text"
        placeholder="请输入手机号"
        value={phone}
        onChange={(e) => setPhone(e.target.value)}
      />
      <input
        type="text"
        placeholder="请输入图形验证码"
        value={captcha}
        onChange={(e) => setCaptcha(e.target.value)}
      />
      <button onClick={handleSendCode} disabled={isLoading}>
        {isLoading ? '发送中...' : '发送验证码'}
      </button>
    </div>
  );
};

export default SmsVerification;

3.2 请求频率限制

在前端限制用户点击“发送验证码”按钮的频率。

import React, { useState, useEffect } from 'react';

const SmsVerification: React.FC = () => {
  const [countdown, setCountdown] = useState(0);

  const handleSendCode = () => {
    // 设置倒计时 60 秒
    setCountdown(60);
    // 发送验证码逻辑...
  };

  useEffect(() => {
    if (countdown > 0) {
      const timer = setTimeout(() => setCountdown(countdown - 1), 1000);
      return () => clearTimeout(timer);
    }
  }, [countdown]);

  return (
    <div>
      <button onClick={handleSendCode} disabled={countdown > 0}>
        {countdown > 0 ? `${countdown}秒后重试` : '发送验证码'}
      </button>
    </div>
  );
};

export default SmsVerification;

4. 后端实现(Node.js + Express)

4.1 使用 Redis 限制请求频率

使用 Redis 记录每个手机号或 IP 的请求频率。

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

app.use(express.json());

// 限制每分钟最多发送 1 次验证码
app.post('/api/send-sms', async (req, res) => {
  const { phone, captcha } = req.body;

  // 验证图形验证码
  if (!validateCaptcha(captcha)) {
    return res.status(400).json({ message: '图形验证码错误' });
  }

  const key = `sms:${phone}`;
  const count = await client.getAsync(key);

  if (count && parseInt(count) >= 1) {
    return res.status(429).json({ message: '请求过于频繁,请稍后再试' });
  }

  // 发送短信验证码
  const code = generateRandomCode();
  await sendSms(phone, code);

  // 记录请求次数
  await client.setAsync(key, 1, 'EX', 60); // 60 秒内只能发送 1 次

  res.json({ message: '验证码已发送' });
});

// 启动服务器
app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

4.2 验证码有效期和使用次数限制

在 Redis 中存储验证码,并设置有效期和使用次数。

app.post('/api/verify-code', async (req, res) => {
  const { phone, code } = req.body;

  const key = `code:${phone}`;
  const storedCode = await client.getAsync(key);

  if (!storedCode || storedCode !== code) {
    return res.status(400).json({ message: '验证码错误或已过期' });
  }

  // 验证成功后删除验证码
  await client.delAsync(key);

  res.json({ message: '验证成功' });
});

4.3 黑名单机制

将频繁请求验证码的 IP 或手机号加入黑名单。

app.post('/api/send-sms', async (req, res) => {
  const { phone, captcha } = req.body;
  const ip = req.ip;

  // 检查黑名单
  const isBlacklisted = await client.getAsync(`blacklist:${ip}`);
  if (isBlacklisted) {
    return res.status(403).json({ message: '您的 IP 已被限制' });
  }

  // 其他逻辑...
});

5. 总结

5.1 核心防御措施

  • 图形验证码:防止自动化脚本。
  • 请求频率限制:限制单位时间内的请求次数。
  • 验证码有效期:设置较短的验证码有效期。
  • 黑名单机制:限制恶意 IP 或手机号。

5.2 注意事项

  • 用户体验:在保证安全的同时,尽量减少对正常用户的干扰。
  • 日志监控:记录验证码请求日志,及时发现异常行为。
  • 多维度限制:结合 IP、设备指纹、用户行为等多维度进行限制。

通过以上措施,可以有效防止手机验证码被刷,保障系统的安全性和稳定性。

相关文章:

  • 【Repos系列】Bandersnatch同步原理
  • docker安装及使用介绍
  • 前端构建工具进化论:从Grunt到Turbopack的十年征程
  • 广播机制(Broadcasting)
  • vue3 前端路由权限控制与字典数据缓存实践(附Demo)
  • STM32F407 cubeIDE Bootloader APP 如何写
  • 【从零开始学习计算机科学】数据库系统(二)关系数据库 与 关系代数
  • AI学习——深度学习核心技术深度解析
  • 时间序列预测(十九)——卷积神经网络(CNN)在时间序列中的应用
  • g++链接及动态库和静态库浅析
  • 2025年Java面试题目收集整理归纳(持续更新)
  • 模板(初阶)
  • Java 浅拷贝和深拷贝
  • 【空间插值】地理加权回归模型(GWR, Geographically Weighted Regression)
  • Windows 发票闪印 PrintPDF-v3.6.10-第三方发票打印辅助工具,无需安装阅读器即可使用
  • 使用 ESP32 和 Python 进行手势识别
  • 蓝桥与力扣刷题(蓝桥 等差数列)
  • Word中把参考文献引用改为上标
  • Linux上位机开发实战(按钮响应)
  • AI绘画软件Stable Diffusion详解教程(10):图生图进阶篇(局部手绘修正)
  • 上海古镇“长效”发展需要提高社会资本参与
  • 甘肃多地发生旱情,三大骨干工程已累计调水2.45亿立方米
  • 商务部新闻发言人就美国企图全球禁用中国先进计算芯片发表谈话
  • 大语言模型在线辩论说服力比人类辩手高出64%
  • 上海浦江游览南拓新航线首航,途经前滩、世博文化公园等景点
  • 取得金奖西瓜品种独家使用权的上海金山,为何要到异地“试种”?