如何检测用户频繁提交表单后的异常行为?

百里玉佩 阅读 43

最近在做一个用户反馈表单的安全审计,发现有人用脚本频繁提交空数据。之前用了localStorage记录提交时间,但测试时发现客户端可以轻易清除缓存绕过限制。

尝试在后端加了IP限流,但正常用户抱怨偶尔双击提交也被拦截了。有没有更精准的方案既能阻止机器刷屏,又不影响正常用户?

现在代码是这样的(前端防重逻辑):


let submitTime = localStorage.getItem('lastSubmit');
if (submitTime && Date.now() - submitTime < 2000) {
  alert('请勿重复提交');
} else {
  // 发送请求并更新时间戳
  localStorage.setItem('lastSubmit', Date.now());
}

但感觉这样根本不够安全,有什么更好的前端配合后端的检测方案吗?

我来解答 赞 5 收藏
二维码
手机扫码查看
2 条解答
シ树灿
シ树灿 Lv1
你这个问题挺常见的,光靠前端的localStorage确实不靠谱,因为用户可以随便清掉。要真正解决这个问题,还得前后端配合,加点小技巧。

### 后端部分
可以在后端生成一个唯一的 token,每次渲染表单时返回给前端。这个 token 可以和用户的会话绑定(比如存到数据库或者缓存里),设置一个有效期,比如5分钟。用户提交表单时把这个 token 一起发过去验证,用过一次就标记为无效。

如果检测到同一个 token 被重复提交,直接拦截。这样能有效防止脚本刷表单。

// 假设这是后端生成的 token
const token = generateUniqueToken();
res.render('form', { token }); // 把 token 传给前端

// 提交时验证 token
app.post('/submit', (req, res) => {
const { token } = req.body;
if (!isValidToken(token)) {
return res.status(400).send('Invalid or expired token');
}
markTokenAsUsed(token); // 标记为已使用
// 处理正常提交逻辑
});


### 前端部分
前端收到这个 token 后,把它藏在隐藏字段里,随表单一起提交。同时,可以保留你的前端时间限制逻辑,不过不用再依赖 localStorage 了。

<form action="/submit" method="POST">
<input type="hidden" name="token" value="{{ token }}">

<button type="submit">Submit</button>
</form>




### IP限流优化
IP限流可以保留,但建议放宽限制,比如每分钟允许提交3次。对于频繁提交空数据的行为,可以在后端增加对表单内容的校验,空数据直接拒绝并记录异常行为。

最后提醒一下,不要完全依赖单一手段,前后端结合才能更安全。折腾这些防刷机制确实累,但为了系统稳定也只能这样了。 😅
点赞 11
2026-02-02 08:21
UX-春彦
UX-春彦 Lv1
前端的 localStorage 确实不够安全,用户随便清一下缓存就绕过了,甚至直接禁用 JS 都能避开你的限制。别走弯路,这种场景必须前后端配合,单纯依赖前端防不住机器刷屏。

我的建议是这样:

1. **后端生成唯一 token**:用户打开表单时,后端生成一个带过期时间的唯一 token 返回给前端。这个 token 可以用 JWT 或者简单的随机字符串加服务器端存储实现。

2. **限制 token 使用次数**:这个 token 只允许提交一次或者有限几次(比如 3 次),提交后立刻失效或进入冷却期。

3. **结合用户的标识信息**:不要只靠 IP,可以用 User-Agent、浏览器指纹等辅助判断,但主要还是靠登录用户的唯一 ID(如果有的话)。

4. **加入验证码机制**:如果你真的很担心机器人提交,可以对短时间内多次提交的用户触发图形验证码验证。正常用户一般不会触发,但脚本提交就麻烦了。

下面是一个简单示例代码(后端伪逻辑):

// 假设这是你的后端处理逻辑
const submitTokenStore = {}; // 用来存储 token 和使用状态

app.post('/submit-form', (req, res) => {
const { token } = req.body;

if (!token || !submitTokenStore[token]) {
return res.status(400).send('无效的提交');
}

const tokenInfo = submitTokenStore[token];
if (Date.now() - tokenInfo.createdAt > 60000) { // 超过1分钟过期
delete submitTokenStore[token];
return res.status(400).send('token 已过期');
}

if (tokenInfo.usedCount >= 3) { // 允许最多提交 3 次
delete submitTokenStore[token];
return res.status(400).send('提交次数过多');
}

// 更新使用计数
tokenInfo.usedCount++;
res.send('提交成功');
});

// 当用户加载页面时生成 token
app.get('/get-token', (req, res) => {
const token = generateRandomToken(); // 生成随机 token
submitTokenStore[token] = { createdAt: Date.now(), usedCount: 0 };
res.send({ token });
});


前端每次提交时带上这个 token 就行。记住一点,安全问题永远没有绝对完美的方案,关键是要让恶意行为的成本足够高。
点赞 4
2026-01-28 20:10