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

embedded/2025/3/17 18:48:28/

防止手机验证码被刷是开发中常见的安全问题,尤其是在涉及用户注册、登录或敏感操作时。攻击者可能会通过自动化脚本频繁请求验证码,导致短信轰炸或资源浪费。以下是如何在 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><inputtype="text"placeholder="请输入手机号"value={phone}onChange={(e) => setPhone(e.target.value)}/><inputtype="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、设备指纹、用户行为等多维度进行限制。

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


http://www.ppmy.cn/embedded/173396.html

相关文章

游戏引擎学习第160天

回顾和今天的计划 我们没有使用任何游戏引擎和库&#xff0c;完全靠我们自己&#xff0c;使用的是老式的编程方式。 我们已经构建了很多内容&#xff0c;游戏引擎开发也慢慢接近尾声。现在我们已经接近完成了所有为支持游戏开发所需要的工作&#xff0c;接下来将逐步过渡到游戏…

git使用指南

git使用指南 工作中常用的 Git 命令&#xff0c;这些基本够用了。能够记住命令的话可以使你的工作效率提高一大截&#xff0c;当然使用可视化的代码编译工具操作也可。管他操作是否高大上&#xff0c;适合你的才是最好的&#xff01; 1. 克隆远程仓库的代码到本地 git clone g…

HTTP 和 HTTPS:从不安全到安全的蜕变之路

HTTP&#xff08;超文本传输协议&#xff09;和 HTTPS&#xff08;超文本传输安全协议&#xff09;是网络上最常用的协议&#xff0c;它们为网页浏览、数据传输提供基础支持。虽然只有一字之差&#xff0c;但 HTTPS 却对 HTTP 做出了巨大的改进&#xff0c;尤其在安全性方面。本…

java泛型通配符?及上下界(extends,super)保证安全性、灵活性、可读性

在 Java 中&#xff0c;泛型通配符&#xff08;?&#xff09;用于表示未知类型&#xff0c;通常用于增强泛型的灵活性。通配符可以与上下限结合使用&#xff0c;以限制泛型的范围。以下是通配符及上下限的使用示例&#xff1a; 1. 无界通配符 (?) 无界通配符表示可以接受任意…

前缀和算法第一弹(一维前缀和和二维前缀和)

目录 前言 1. 一维前缀和 &#xff08;1&#xff09;题目及示例 ​编辑 &#xff08;2&#xff09;暴力解法 &#xff08;3&#xff09;算法优化 2. 二维前缀和 &#xff08;1&#xff09;题目及示例 &#xff08;2&#xff09;暴力解法 &#xff08;3&#xff09;算…

vscode接入DeepSeek 免费送2000 万 Tokens 解决DeepSeek无法充值问题

1. 在vscode中安装插件 Cline 2.打开硅基流动官网 3. 注册并登陆&#xff0c;邀请码 WpcqcXMs 4.登录后新建秘钥 5. 在vscode中配置cline (1) API Provider 选择 OpenAI Compatible &#xff1b; (2) Base URL设置为 https://api.siliconflow.cn](https://api.siliconfl…

MySQL时间溢出原理、影响与解决方案

一、问题背景与现象复现 操作场景&#xff1a; 本文将手把手带您了解mysql时间溢出原理、实战影响与全面解决方案&#xff0c;所有代码均通过dblens for mysql数据库工具验证&#xff0c;推荐使用该工具进行可视化数据库管理和开发。 在MySQL 5.7环境中&#xff0c;若通过命令…

JetBrains(全家桶: IDEA、WebStorm、GoLand、PyCharm) 2024.3+ 2025 版免费体验方案

JetBrains&#xff08;全家桶: IDEA、WebStorm、GoLand、PyCharm&#xff09; 2024.3 2025 版免费体验方案 前言 JetBrains IDE 是许多开发者的主力工具&#xff0c;但从 2024.02 版本起&#xff0c;JetBrains 调整了试用政策&#xff0c;新用户不再享有默认的 30 天免费试用…