Introduction
I’m paraphrasing OWASP here, but broken access control is a serious vulnerability that allows an attacker to access or manipulate resources they shouldn’t be able to. The most obvious one of the top of my head is where an attacker grabs some resource id that belongs to a different user and uses it to access that resource.
It’s a very broad category of vulnerabilities that include things such as:
- Privilege escalation
- Bypassing access control checks via URI modification
- CORS misconfiguration allowing access from unauthorised/untrusted origins (exfiltration of data etc.)
- and much more.
In this short blog post, I will demonstrate how a Spring Boot application can be vulnerable to a particular type of broken access control vulnerability. This vulnerability is one where a user can access resources that belong to other users by simply modifying the URI to include the id of the resource they want to access.
The demo is available at GitHub. In this demo I use HTTPie to demonstrate the vulnerability, but you can use any HTTP client of your choice.
Demo App
The app is simple spring boot app that uses H2 database and Spring Security to secure the endpoints. Users can own many pets, but our intention is that for each user to only see their pets’ information. We use HTTP Basic authentication to authenticate users, and the app is secured with Spring Security. This is for simplicity but the concepts would apply to any authentication mechanism.
What can go Wrong?
The demo app is initially vulnerable to broken access control because it does not enforce any access control. Leading users to see other users’ pets by simply modifying the URI to include the id of the owner they want to see their pets.
user1 has the following pets:
[
{
"age": 3,
"id": 1,
"name": "Buddy"
},
{
"age": 5,
"id": 2,
"name": "Max"
}
]
and user2 has the following pets:
[
{
"age": 2,
"id": 3,
"name": "Bella"
},
{
"age": 4,
"id": 4,
"name": "Charlie"
}
]
When user1
tries to access their pets, they can do so by making a GET request to /api/pets/1
. The application returns the pets that belong to user1
.
In this scenario, user1
can access user2
’s pet by simply changing the URI from /api/pets/1
to /api/pets/2
. This is a classic example of broken access control where the application does not enforce any access control checks.
Given the HTTPie command below, you can see that user1
can access user2
pets by simply changing the id in the URI.
http -b -a user1@example.com:password GET http://localhost:8080/api/pets/2
Which displays
[
{
"age": 2,
"id": 3,
"name": "Bella"
},
{
"age": 4,
"id": 4,
"name": "Charlie"
}
]
This is the controller’s code that allows this to happen:
@GetMapping("/{ownerId}")
public List<Pet> getPetsByOwnerId(@PathVariable("ownerId") Long ownerId) {
return petService.getPetsByOwnerId(ownerId);
}
Looks like the dev was too lazy to implement the access control check, so they just returned all pets for the given owner id. Classic rookie mistake.
How do we mitigate it?
So how do we mitigate against this? We can use Spring Security @PreAuthorize
annotation to enforce access control checks. This way we can ensure that users see their own pets only.
The result would be something like this:
@PreAuthorize("@userService.getUserIdByEmail(authentication.name) == #ownerId")
@GetMapping("/{ownerId}")
public ResponseEntity<?> getPetsByOwnerId(@PathVariable("ownerId") Long ownerId) {
return ResponseEntity.ok(petService.getPetsByOwnerId(ownerId));
}
Summary
Broken access control is a ubiquitous vulnerability that can lead to some very serious consequences if not mitigated properly. Thankfully, Spring security provides ways to mitigate against it with some nifty annotations like @PreAuthorize
.
You can play around with the demo and I hope you took something away from this post that you can apply to your own applications.
For further reading on this type of vulnerability see the OWASP: Insecure Direct Object Reference Prevention Cheat Sheet.