一、为什么要做RESTful API的安全检测

现在不管是APP、小程序还是网站后台,几乎都靠RESTful API传数据——比如你点进个人中心看订单、改收货地址,本质都是APP给后台发了个带参数的请求,后台处理后返回数据。但很多开发做API时,只想着“功能能不能跑通”,忘了加安全防护,结果就出大问题。 比如有人能直接改请求里的参数,看别人的订单;或者把普通用户的权限改成管理员,删别人的数据。这种漏洞叫“越权”和“参数篡改”,要是没提前查出来,上线后要么用户数据泄露,要么平台被搞乱,最后都是开发背锅。 做安全检测不用等上线,开发阶段就能做,用工具自动化测,比人工快太多,也能覆盖到人工容易漏的地方。

二、核心工具:Burp Suite的基础用法

做API安全检测的工具很多,Burp Suite是最常用的,它能抓API请求、改参数、自动测漏洞,适合新手也适合老手。用之前先搞懂两个核心功能:

2.1 抓包:截获API请求

正常你用APP或网页操作时,请求直接发给后台,你看不到也改不了。Burp的抓包功能就是当“中间人”,把请求先截下来,让你能改参数再发出去。 比如你改个人资料时,发的请求是/api/user/123/update,后台会检查你是不是123号用户,要是你把123改成456,再发请求,后台没拦的话,你就能改别人的资料。

2.2 重放:重复发请求

抓包后可以把请求存下来,反复发,不用再操作APP或网页,适合测漏洞时重复验证。

三、实战1:用Burp测越权漏洞

越权漏洞分两种:垂直越权(普通用户变管理员)、水平越权(普通用户看别人的个人数据),先测水平越权,这个最常见。

3.1 准备工作:搭建测试环境

先搭一个简单的测试后台,用Node.js+Express,代码很简单,只有两个接口:登录和获取订单。 技术栈:Node.js 18 + Express 4 + JSON Web Token(JWT)

// 安装依赖:npm install express jsonwebtoken cors
const express = require('express');
const jwt = require('jsonwebtoken');
const cors = require('cors');
const app = express();

// 中间件:解析JSON、允许跨域
app.use(express.json());
app.use(cors());

// 测试用户数据:用户ID=1的订单,用户ID=2的订单
const users = [
  { id: 1, username: 'user1', password: '123456', orders: [{ orderId: 101, goods: '手机' }] },
  { id: 2, username: 'user2', password: '123456', orders: [{ orderId: 102, goods: '电脑' }] }
];
const JWT_SECRET = 'test-secret-key'; // JWT密钥,实际项目要存环境变量

// 登录接口:返回JWT令牌
app.post('/api/login', (req, res) => {
  const { username, password } = req.body;
  const user = users.find(u => u.username === username && u.password === password);
  if (!user) return res.status(401).json({ code: 401, msg: '账号密码错误' });
  // 生成JWT,存用户ID,过期时间1小时
  const token = jwt.sign({ userId: user.id }, JWT_SECRET, { expiresIn: '1h' });
  res.json({ code: 200, msg: '登录成功', token });
});

// 获取订单接口:根据userId参数查订单(漏洞点:没验证当前登录用户的userId)
app.get('/api/getOrders', (req, res) => {
  const { userId } = req.query; // 从请求参数拿userId
  const user = users.find(u => u.id === Number(userId));
  if (!user) return res.status(404).json({ code: 404, msg: '用户不存在' });
  res.json({ code: 200, data: user.orders });
});

// 启动服务,监听3000端口
app.listen(3000, () => console.log('服务启动,地址:http://localhost:3000'));

这个后台的漏洞很明显:获取订单时,只看参数里的userId,没检查当前登录的用户是不是这个userId的主人。比如你登录user1(userId=1),把参数改成userId=2,就能拿到user2的订单。

3.2 测试步骤

第一步:配置Burp抓包 打开Burp,切换到Proxy(代理)标签页,点Options(选项),找到Intercept Client Requests(拦截客户端请求),把端口改成8080,勾选Intercept(拦截)。然后把浏览器的代理改成127.0.0.1:8080,这样浏览器发的请求会先到Burp。 第二步:登录拿令牌 用浏览器访问http://localhost:3000,发登录请求:

{
  "username": "user1",
  "password": "123456"
}

Burp会截到这个请求,点Forward(转发),后台返回token,把这个token记下来,后面要用到。 第三步:抓获取订单的请求 发获取订单的请求,参数是userId=1,Burp截到后,切换到Repeater(重放)标签页,这个请求会自动存到Repeater里。 第四步:改参数测越权 把参数里的userId改成2,点Send(发送),看返回结果:如果返回了user2的订单,说明有水平越权漏洞。

3.3 漏洞修复

修复的核心是:不能相信参数里的userId,要从JWT里拿当前登录的用户ID,和参数里的userId对比。修改获取订单接口的代码:

// 修复后的获取订单接口
app.get('/api/getOrders', (req, res) => {
  const { userId } = req.query;
  const token = req.headers.authorization?.split(' ')[1]; // 从请求头拿JWT
  if (!token) return res.status(401).json({ code: 401, msg: '未登录' });
  try {
    // 验证JWT,拿到当前登录的userId
    const decoded = jwt.verify(token, JWT_SECRET);
    const currentUserId = decoded.userId;
    // 对比:参数里的userId必须等于当前登录的userId
    if (Number(userId) !== currentUserId) {
      return res.status(403).json({ code: 403, msg: '无权限查看该用户的订单' });
    }
    const user = users.find(u => u.id === Number(userId));
    if (!user) return res.status(404).json({ code: 404, msg: '用户不存在' });
    res.json({ code: 200, data: user.orders });
  } catch (err) {
    return res.status(401).json({ code: 401, msg: '令牌无效' });
  }
});

修复后再测,改userId=2发请求,会返回无权限,漏洞就堵上了。

四、实战2:用Burp测参数篡改漏洞

参数篡改是指攻击者改请求里的参数值,比如把商品价格从100改成1,把订单状态从“待支付”改成“已完成”。

4.1 准备工作:新增测试接口

在原来的Node.js后台里加一个更新订单状态的接口,漏洞点是:没验证参数的合法性,比如订单状态的取值范围。

// 新增:更新订单状态接口(漏洞点:没验证status参数的取值)
app.post('/api/updateOrderStatus', (req, res) => {
  const { orderId, status } = req.body; // 从请求体拿参数
  const token = req.headers.authorization?.split(' ')[1];
  if (!token) return res.status(401).json({ code: 401, msg: '未登录' });
  try {
    const decoded = jwt.verify(token, JWT_SECRET);
    const currentUserId = decoded.userId;
    // 先找当前用户的订单
    const user = users.find(u => u.id === currentUserId);
    const order = user.orders.find(o => o.orderId === Number(orderId));
    if (!order) return res.status(404).json({ code: 404, msg: '订单不存在' });
    // 漏洞:直接把参数里的status赋值给订单
    order.status = status;
    res.json({ code: 200, msg: '更新成功', data: order });
  } catch (err) {
    return res.status(401).json({ code: 401, msg: '令牌无效' });
  }
});

正常情况下,订单状态应该只有“待支付”“已支付”“已完成”,但这个接口没限制,攻击者可以传任何值,比如“已发货”“已退款”。

4.2 测试步骤

第一步:抓更新订单的请求 用user1登录,发更新订单的请求,内容是:

{
  "orderId": 101,
  "status": "已支付"
}

Burp截到后,转到Repeater。 第二步:改参数测篡改 把status改成“已退款”,点Send,看返回结果:如果返回更新成功,说明有参数篡改漏洞。

4.3 漏洞修复

修复的核心是:对参数做合法性校验,比如限制status的取值范围。修改接口代码:

// 修复后的更新订单状态接口
app.post('/api/updateOrderStatus', (req, res) => {
  const { orderId, status } = req.body;
  const token = req.headers.authorization?.split(' ')[1];
  if (!token) return res.status(401).json({ code: 401, msg: '未登录' });
  try {
    const decoded = jwt.verify(token, JWT_SECRET);
    const currentUserId = decoded.userId;
    const user = users.find(u => u.id === currentUserId);
    const order = user.orders.find(o => o.orderId === Number(orderId));
    if (!order) return res.status(404).json({ code: 404, msg: '订单不存在' });
    // 修复:校验status的取值范围,只有这三个值才合法
    const allowedStatus = ['待支付', '已支付', '已完成'];
    if (!allowedStatus.includes(status)) {
      return res.status(400).json({ code: 400, msg: '无效的订单状态' });
    }
    order.status = status;
    res.json({ code: 200, msg: '更新成功', data: order });
  } catch (err) {
    return res.status(401).json({ code: 401, msg: '令牌无效' });
  }
});

修复后再测,传“已退款”会返回无效状态,漏洞堵上。

五、实战3:用Burp自动化测试所有漏洞

前面的测试是手动改参数,要是接口多、参数多,手动测太慢,Burp有个功能叫Intruder(入侵),能自动改参数,批量测漏洞。

5.1 用Intruder测水平越权

比如要测多个userId的情况,不用手动改,让Burp自动生成不同的userId参数: 第一步:把Repeater里的获取订单请求转到Intruder,切换到Positions(位置)标签页,找到参数里的userId=1,点Add(添加),把这个位置标记为测试点。 第二步:切换到Payloads(载荷)标签页,在Payload Type(载荷类型)里选Numbers(数字),然后设置范围:从1到100,步长1。这样Burp会自动生成1、2、3…100的userId参数。 第三步:点Start Attack(开始攻击),Burp会批量发请求,每个请求用不同的userId。 第四步:看结果:要是某个请求的返回码是200,并且返回了订单数据,说明这个userId的订单能被当前用户访问,存在越权漏洞。

5.2 用Intruder测参数篡改

比如要测status参数的所有可能值,不用手动改,让Burp自动传不同的status: 第一步:把更新订单的请求转到Intruder,标记status参数的位置。 第二步:Payload Type选List(列表),然后把所有可能的恶意值填进去,比如“已退款”“已发货”“已取消”。 第三步:开始攻击,看结果:要是某个恶意值的请求返回200,说明存在参数篡改漏洞。

六、应用场景、优缺点、注意事项

6.1 应用场景

这种安全检测适合所有用RESTful API的项目,比如电商平台(防止用户看别人订单、改价格)、社交APP(防止用户改别人的个人资料)、企业后台(防止普通员工改管理员数据)。开发阶段测,能提前发现漏洞,避免上线后出问题;上线后定期测,能防止新接口加了没做防护。

6.2 技术优缺点

优点:用Burp自动化测,速度快,能覆盖人工容易漏的参数,不用写复杂的测试代码,适合不同基础的开发。缺点:Burp的免费版功能有限,比如Intruder的攻击速度慢;要是接口需要复杂的参数(比如加密的请求),需要手动处理;测试结果需要人工分析,不能自动判断漏洞的严重程度。

6.3 注意事项

第一:测试环境要和生产环境一致,比如JWT密钥、数据库数据,不然测试结果不准。第二:不能用生产环境测,不然会影响真实用户的数据。第三:测试前要备份测试环境的数据,防止测试时把数据改乱。第四:除了Burp,还要结合代码检查,比如用代码扫描工具查有没有没做权限校验的接口。

七、文章总结

RESTful API的安全检测,核心是测“越权”和“参数篡改”,用Burp Suite能快速上手,不管是手动改参数还是自动化批量测,都能覆盖常见的攻击面。开发时要记住两个核心原则:一是不能相信参数里的身份信息(比如userId),要从令牌里拿真实的身份;二是所有参数都要做合法性校验,限制取值范围。提前做安全检测,不仅能避免上线后的风险,还能让项目更稳定,也能提升自己的安全意识。