Broken access control on invoice endpoints (IDOR)
- CVSS 3.1
- 8.1 · AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
- Category
- OWASP A01:2025 Broken Access Control · CWE-639
- Affected
- GET, PUT /api/v2/invoices/{id}
- Status
- Fixed, verified in retest
Description
The invoice API checks that a request comes from a logged-in user, but not that the invoice requested belongs to that user. Invoice IDs are sequential, so every invoice in the system can be reached by changing the number in the URL.
Impact
Any customer, including one who signed up for a free trial, could read the names, postal addresses, line items and totals on every other customer’s invoices, and change their contents. If exploited, this would be a reportable personal-data breach under GDPR and similar laws.
Evidence
Logged in as test user A, a request for an invoice owned by test user B returned B’s data:
GET /api/v2/invoices/10482 HTTP/1.1
Host: app.acme-invoicing.example
Authorization: Bearer [user A token, redacted]
HTTP/1.1 200 OK
{ "id": 10482, "customer_id": 311, // user B, not user A (customer_id 207)
"billing_name": "[redacted]", "total": "1,240.00", ... } Remediation
-
Scope every invoice query to the current user’s account on the server, e.g. look up invoices by
idandcustomer_idfrom the session, never from the request. - Apply the same ownership check to update and delete handlers, not only reads.
- Add automated tests that request another user’s objects and expect 404.
- Consider non-sequential identifiers (UUIDs) as defense in depth, not as the fix.
Retest
Retested after the fix: requests for invoices owned by other customers now return
404 Not Found for both read and update. Closed.