In the previous article, we had them build our ground around OWASP’s top ten projects and covered the brief official definition of OWASP Top 10 for API security.
In this article, we will explore the first of the OWASP Top 10 API security risks for the year 2019. (API1:2019 - Broken object-level authorization).
In most of the API implementations, where we have to get some data which is specific to some object, Reference of the internal object implementation is exposed, for example: “id”, “pid”, “uid” and so on. Although in most cases this reference is visible as part of the HTTP parameter itself, but these could also be part of headers and cookies.
The problem here is that it reveals the real identifier and format/pattern used of the element in the storage backend side. The most common example of it (although is not limited to this one) is a record identifier in a storage system (database or filesystem).
Once the pattern is identified, the malicious user can easily guess/autogenerate the reference ID’s and modify the requests to supply the reference ID’s that do not belong to them.
If the API implementation does not have proper measures in place, then these malicious requests can cause revelations, modifications, or even deletion of records in the backend.
Just because the direct object reference checks were not in place or misconfigured. That is the reason this attack is also known as Insecure Direct Object Reference or IDOR in short.
Let us assume that we have an API implementation that gets the financial information of a user. And to get the details the API expects the user id as parameter.
If we have to summarize then we can say, IDOR is a combination of two issues
So, the solution to prevent IDOR completely has to address both the issues, Below are some of the recommendations.
As we saw earlier that our solution has to address both the Access and predictable ID issue. Let us try to build an application to demonstrates the same. Please be advised that this application is meant for demonstration purposes only.
So, our hypothetical application has a requirement that the users shall be able to get his data (e.g. financial details). Now, the API design could take user-id as input and serve returns the financial details belonging to the user. The common implementation approach for the API will be to have the user id as path parameters or as part of headers. Let’s say we choose the path parameter. Then our API will look something like this. And it works as expected.
PowerShell
1
http://localhost:9999/getdata-IDOR-vulnerable/user_2
But by analyzing the request we can see that the input is user_1, what if we supply **user_2? **
PowerShell
1
http://localhost:9999/getdata-IDOR-vulnerable/user_2
Voila! it works again. Thus, it is clear that **user_x ** is a reference to internal ID. Now, what if we somehow automate this parameter using some tool, or even simple programming? We can get the data of all the users available in the system.
PowerShell
1
http://localhost:9999/getdata-IDOR-vulnerable/user_2
2
http://localhost:9999/getdata-IDOR-vulnerable/user_3
3
http://localhost:9999/getdata-IDOR-vulnerable/user_4
4
.
5
.
6
.
7
http://localhost:9999/getdata-IDOR-vulnerable/user_N
We leaked all the data just because someone was able to guess the reference ID.
So, what if we use something difficult or random to guess? We solve one part of our problem.
One of the possible ways could be that we use these complex ID’s (something like UUID) in our backend system, or we can wrap our data fetch logic around some utility class or filter which does this job for each request. In a very basic example, we could write something like below.
Java
1
package org.sk.owasp.api.security.idor.util;
2
3
import java.util.Base64;
4
import org.springframework.stereotype.Service;
5
/**
6
* <strong>FOR DEMONSTRATION ONLY, NOT TO BE USED IN PRODUCTION.</strong><br>
7
* As this code is for demonstration purpose only. {@link Base64} is used to encypt-decrypt the ID's. This algorithm is not considered safe and can be
8
* decoded easily.
9
* In real world scenario you shall be using more sophisticated algorithms or something like UUID
10
* Access check in intentionaly left from the code.
11
* @author Satish Sharma
12
*
13
*/
14
@Service
15
public class IdorUtility {
16
17
// this could be externalized to configuration.
18
private String saltString="S3cUr#d1Ds1L$";
19
20
public String computeObfuscatedId(String actualIdentifier) {
21
String saltedString = actualIdentifier + saltString;
22
return new String(Base64.getEncoder().encode(saltedString.getBytes()));
23
}
24
25
public String resolveObfuscatedId(String obfuscatedIdentifier) {
26
var decodedBit = Base64.getDecoder().decode(obfuscatedIdentifier);
27
String decodedSaltedString = new String(decodedBit);
28
return decodedSaltedString.replace(saltString, "");
29
}
30
}
Once we wrap our service call around the encode-decode utility class. As we have encoded all the identifiers with Base64, The API request will change something like below. Notice the change in the path parameter in the URL.
PowerShell
1
http://localhost:9999/getdata-IDOR-secured/dXNlcl8zUzNjVXIjZDFEczFMJA%3D%3D
As we can see now the IDs cannot be guessed easily. Combined the above code with authorization mechanism (left intentionally from this code). We address both the issues, and hence prevent the IDOR.
You can find the sample spring-boot application at this GitHub location. In the next article, we shall be addressing **API2:2019 Broken User Authentication. **
Thank you for reading. If any questions or comments please share them, I would be happy to hear.
#security #spring boot 2 #owasp top 10 #api penetration testing #owasp top 10 web security risk #broken api