API1:2019 Broken Object Level Authorization
In case you're landing here coming from a search engine or a referral article, you may want to read our OWASP API Security Top 10 series debut article first. This is the first article in this series because it was, and probably still is, the most critical API security risk at the time the document was first published (late 2019). You may know it already as Insecure Direct Object Reference (IDOR). Since the name was not that accurate, the OWASP API Security Project team decided to get it right for APIs.
What is the issue?
A user who is not explicitly authorized can access someone else's resources. Let's make a few things clear in this statement:
- "user": whoever can issue requests against the API server and receive the corresponding response;
- "not explicitly authorized": access is granted due to a design/implementation flaw;
- "access": either read or modify other user's data.
How does it look like?
Let's say we're dealing with a ToDo lists management API that backs a web application front-end. We know nothing about this particular API but, we see that to retrieve a single task the browser issues the following request, and corresponding response:
Based on the API's request and response, we know now that the number 63249 in the URL is, in fact, the task's unique identifier ("id"). Nothing prevents us to issue a specially crafted request using a different task identifier, for example, 63250, the next positive integer, and check the response:
Comparing both responses, it's noticeable that, apart from task-specific data (e.g. id, timestamp, title, and description), "userId" values don't match. If the first one (23643) sent as part of the response of the legit request was ours, then the second one (10242) makes it clear that we're accessing someone else's task.
It is important to realize that the problem doesn’t rely on using sequential numbers as task identifiers. Using Universally Unique Identifier (UUID) or any other random string is as secure as the sequential numbers: replacing one with the other won't solve the underlying issue. Non-sequential identifiers may be harder to guess, unless you find, for example, another API endpoint that leaks them. On that matter, you can check the MindAPI Broken Object Level Authorization testing branch.
So, what is the real problem? Simply a missing or faulty authorization check, responsible to determine whether the authenticated agent (e.g. the user) is allowed to access a specific resource (e.g. task ID 63250).
Where have we seen this issue lately?
This is not only the most critical API security risk but also a common one. Unfortunately, we find it often in the wild and on some of our penetration testing assessments. Recently Checkmarx disclosed a Broken Object-Level Authorization issue on Coursera's API. Exploiting this vulnerability, attackers could not only retrieve any user's preferences but also set them. No authentication was required. Instagram's GraphQL API had the same issue, giving unauthorized users access to private and archived posts and stories as long as the attacker knew or brute-forced the ID.
Authorization and access control mechanisms tend to be complex and typically not amenable to automated static or dynamic testing. Even when a proper authorization infrastructure is deployed, developers may forget to perform the required checks before running the business logic on a specific API endpoint.
Detailed security requirements for each API feature, use of a well-tested and actively maintained authorization mechanism, code review on codebase changes, good testing coverage, and a properly configured CI/CD pipeline help tackling authorization issues. Even if all these recommendations are satisfied, manual testing should be performed: QA (Quality Assurance) and internal security teams can do the job, but an external security audit will help to increase confidence in the system.