Hify 预约演示
示意性示例报告

一张发票被返回给另一个账户。

这是一个虚构例子,说明一条发现如何从证据记录到复测。域名、账户和数据都是编造的。

目标
app.example.com, api.example.com/v1
测试类型
外部测试,使用测试账户
状态
等待贵方团队修复

范围

范围内:app.example.com 和 api.example.com/v1,使用两个已授权的测试账户。按约定排除:第三方支付页面。身份验证在确定范围时已确认,这次外部测试不需要源代码。

测试账户

测试账户 A

session=test-account-a

拥有发票 demo-01

测试账户 B

session=test-account-b

拥有发票 demo-02

请求和响应

以测试账户 A 登录后,我们请求了一张属于测试账户 B 的发票。

请求
GET /api/invoices/demo-02 HTTP/1.1
Host: api.example.com
Cookie: session=test-account-a
响应200 OK
{ "invoice": "demo-02",
  "owner": "test account B"}

API 把账户 B 的发票返回给了账户 A。研究人员在报告之前复现了这个结果。

复现步骤

  1. 在 app.example.com 以测试账户 A 登录。
  2. 发送GET /api/invoices/demo-02 使用账户 A 的会话。
  3. 看到包含账户 B 发票的 200 响应。
  4. 再以账户 B 重复一次,确认 demo-02 属于 B。

代码上下文 Defense 的拟定范围

路径:Web 应用,然后是账单服务,然后是发票查询。处理函数按 id 加载发票并返回,没有核对所有者。

export async function getInvoice(req, res) {
  const invoice = await invoices.find(req.params.id)
  return res.json(invoice)   // owner not compared}

建议做法:在返回发票之前,把发票所有者与当前登录账户做比较。

开发人员复核

贵方开发人员复核指导并决定如何修改。一种可能的写法:

  const invoice = await invoices.find(req.params.id)
+ if (!invoice || invoice.accountId !== req.session.accountId) {+   return res.status(403).end()+ }  return res.json(invoice)

复测预期

在贵方团队修复之后,我们在约定范围内重复同一条已授权请求。

在贵方团队修复后的预期:403
账户 A 自己的发票仍应返回 200。
403