为什么永远不要相信前端输入?绕过前端验证,只需一个 cURL 命令!

大家好😁。

上个月 Code Review,我拦下了一个新人的代码。

他写了一个转账功能,前端做了极其严密的校验:

  • 金额必须是数字。
  • 金额必须大于 0。
  • 余额不足时,提交按钮是 disabled 的。
  • 甚至还写了复杂的正则表达式,防止输入负号。

他自信满满地跟我说:老大,放心吧,我前端卡得死死的,用户绝对传不了非法数据。

我笑了笑🤣,没看他的后端代码,直接打开终端,敲了一行命令。

0.5 秒后,他的数据库里多了一笔"-10000"的转账记录,余额瞬间暴涨!

他看着屏幕,目瞪口呆:这......你是怎么做到的?我按钮明明置灰了啊!

今天,我就来揭秘这个所有后端(和全栈)工程师必须铭记的第一铁律:

前端验证,在黑客眼里,只是个小case🤔。


我是如何羞辱前端验证的

假设我们有一个购物网站,前端有一个简单的购买表单。

前端逻辑(看似完美):

JavaScript 复制代码
// Front-end code
function submitOrder(price, quantity) {
  // 1. 校验价格不能被篡改
  if (price !== 999) {
    alert("价格异常!");
    return;
  }
  // 2. 校验数量必须为正数
  if (quantity <= 0) {
    alert("数量必须大于0!");
    return;
  }
  
  // 发送请求
  api.post('/buy', { price, quantity });
}

你看,用户在浏览器里确实没法作恶。他改不了价格,也填不了负数。

但是黑客,从来不用浏览器点你的按钮。

第一步:打开DevTools Network 面板,正常点一次购买按钮。捕获到了这个请求。

第二步:请求上右键 -> 复制 -> cURL 格式复制。

这一步,我已经拿到了你发送请求的所有密钥:URL、Headers、Cookies、以及那个看似合法的 Data。

第三步:打开终端(Terminal),粘贴刚才复制的命令。但是,我并没有直接回车。

我修改了 --data-raw 里的参数:

  • "price": 999 改成了 "price": 0.01
  • 或者把 "quantity": 1 改成了 "quantity": -100
Bash 复制代码
# 经过魔改后的命令
curl 'http://localhost:3000/user/buy' \
  -H 'Cookie: session_id=...' \
  -H 'Content-Type: application/json' \
  --data-raw '{"price": 0.01, "quantity": 10}' \
  --compressed

回车!

服务器返回:{ "status": "success", "msg": ok!" }

恭喜你,你的前端验证毫发无损,但你的数据库已经被我击穿了。 我用 1 分钱买了 10 个商品,或者通过负数数量,反向刷了库存。


为什么前端验证, 防不了小人🤔

很多新人最大的误区,就是认为用户只能通过我的 UI 来访问我的服务器。

错!大错特错!

Web 的本质是 HTTP 协议。

HTTP 协议是无状态的、公开的。任何能够发送 HTTP 请求的客户端,都是你的用户。

  • Chrome 是客户端。
  • cURL 是客户端。
  • Postman 是客户端。
  • Python 的 requests 脚本也是客户端。
  • node 的 http 脚本也是客户端

前端代码运行在用户的电脑上。

这意味着,用户拥有对前端代码的绝对控制权

  • 他可以禁用 JS。
  • 他可以在 Console 里重写你的校验函数。
  • 他可以拦截请求(用 Charles/Fiddler)并修改数据。
  • 他甚至可以完全抛弃浏览器,直接用脚本轰炸你的 API。

所以,前端验证的唯一作用,是提升用户体验 (比如提示用户格式不对😂),而不是提供安全性😖。


后端该如何防御?(不要裸奔)

既然前端不可信,后端(或 BFF 层)就必须假设所有发过来的数据都是有毒的

1. 永远不要相信 Payload 里的关键数据

前端只传 productId。后端拿到 ID 后,去数据库里查这个商品到底多少钱。永远以数据库为准。

2. 使用 Schema 校验库(Zod / Joi / class-validator)

不要在 Controller 里写一堆 if (req.body.age < 0)。

使用专业的 Schema 校验库,定义好数据的规则。

TypeScript代码👇:

TypeScript 复制代码
// 使用 Zod 定义后端校验规则
const OrderSchema = z.object({
  productId: z.string(),
  // 强制要求 quantity 必须是正整数,拦截 -100 这种攻击
  quantity: z.number().int().positive(), 
  // 注意:这里根本不接收 price 字段,防止被注入
});

// 如果校验失败,直接抛出 400 错误,逻辑根本进不去
const data = OrderSchema.parse(req.body); 

3. 权限与状态校验

不要只看数据格式对不对,还要看人对不对。

  • 这个用户有权限买这个商品吗?
  • 这个订单现在的状态允许支付吗?(防止重复支付攻击🤔)

还有一种更高级的攻击:Replay Attack(重放攻击)

你以为校验了数据就安全了?

如果我拦截了你一次领优惠券的请求,虽然我改不了数据,但我可以用 cURL 连续运行 1000 次这个命令。

Bash 复制代码
# 一个简单的循环,瞬间刷爆你的接口
for i in {1..1000}; do curl ... ; done

如果你的后端没有做幂等性(Idempotency)校验或频率限制(Rate Limiting) ,那我瞬间就能领走 1000 张优惠券。

防御手段👇:

  • Redis 计数器:限制每个 IP/用户 每秒只能请求几次。
  • 唯一 Request ID:对于关键操作,要求前端生成一个 UUID,后端处理完后记录下来。如果同一个 UUID 再次请求,直接拒绝。

对于前端安全,所有的输入都是可疑的🤔

作为全栈或后端开发者,当你写 API 时,请忘掉你那个漂亮的前端界面。

你的脑海里应该只有一幅画面:

屏幕对面,不是一个点鼠标的用户,而是一个正在敲 cURL 命令的黑客。

只有这样,你的代码才算真正安全了😒。

相关推荐
深盾安全1 小时前
Linux 跨进程内存操作:三种实战方法与攻防思考
安全
嘴平伊之豬1 小时前
对照typescript学习鸿蒙ArkTS
前端·harmonyos
奋斗猿1 小时前
前端实测:RSC不是银弹,但它真的重构了我的技术栈
前端·react.js
玄魂1 小时前
VChart 扩展新功能:一行代码解锁数据回归与趋势分析
前端·echarts·数据可视化
AndyGoWei1 小时前
pnpm 是什么,看这篇文章就够了
前端·javascript
zl0_00_01 小时前
isctf2025 部分wp
linux·前端·javascript
西洼工作室1 小时前
移动开发常见问题
前端·css3·web移动开发
同学807962 小时前
新版本Chrome谷歌浏览器访问本地网络请求跨域无法正常请求
前端·http
儿歌八万首2 小时前
Jetpack Compose 实战:打造高性能轮播图 (Carousel) 组件
android·前端·kotlin