Hify Book a demo
Illustrative sample report

An invoice is returned to another account.

A fictional example of how a finding is documented, from evidence to retest. Domains, accounts and data are invented.

Targets
app.example.com, api.example.com/v1
Test type
External, with test accounts
Status
Awaiting your team's correction

Scope

In scope: app.example.com and api.example.com/v1, tested with two authorized test accounts. Excluded by agreement: third-party payment pages. Authentication was confirmed during scoping, and no source code was needed for this external test.

Test accounts

Test account A

session=test-account-a

Owns invoice demo-01

Test account B

session=test-account-b

Owns invoice demo-02

Request and response

Signed in as test account A, we requested an invoice that belongs to test account B.

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

The API returned account B's invoice to account A. A researcher reproduced the result before reporting it.

Steps to reproduce

  1. Sign in to app.example.com as test account A.
  2. Send GET /api/invoices/demo-02 with account A's session.
  3. Observe a 200 response containing account B's invoice.
  4. Repeat as account B to confirm that demo-02 belongs to B.

Code context Proposed Defense scope

Trace: web app, then billing service, then the invoice lookup. The handler loads an invoice by id and returns it without checking its owner.

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

Suggested approach: compare the invoice owner with the signed-in account before returning it.

Developer review

Your developers review the guidance and decide on the change. One possible version:

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

Retest expectation

After your team's correction, we repeat the same authorized request within scope.

Expected after your team's correction: 403
Account A's own invoices should still return 200.
403