DEV Community

loading...

Stupid but common security vulnerability in web app

franzwong profile image Franz Wong ・2 min read

Alt Text

Image from https://www.irasutoya.com/

I can't remember how many times I see this security vulnerability. When we design REST API, we usually put some IDs in URL. For example, we have a REST API returning user profile.

GET /users/1234
Enter fullscreen mode Exit fullscreen mode

There is nothing wrong with the design. The problem is people forgetting to check the ID in the URL against the ID in the cookie or JWT. It means that even I log in as user 1234, I am able to get the profile of user 7890.

There is another case when we have multiple IDs in URL. Just like a REST API returning transaction details.

GET /users/1234/transactions/123-456-789
Enter fullscreen mode Exit fullscreen mode

It is not enough to check user ID only. I can do something like this.

GET /users/1234/transactions/987-654-321
Enter fullscreen mode Exit fullscreen mode

987-654-321 is the transaction ID not belonging to user 1234. We must check the ownership or access permission of the item.

Things can go further complicated if we allow some users (e.g. staff, admin) to access other users' information. But I don't want to talk about it here.

I heard people arguing that user is not able to access URL containing other user ID through the FRONTEND, so it is safe not to check. But of course, we all know a simple curl command can do it.

CHECK ANYTHING FROM FRONTEND

To be more defensive, we can simply not putting user ID in URL and let the backend to get the ID from the cookie.

GET /users
Enter fullscreen mode Exit fullscreen mode

But for the item ownership case, we can't use this trick.

Of course, if you want to apply load balancing or analysis based on URL, this design is not good for you too.

This vulnerability is really stupid you may think. I think so too. But we can also see this problem from another point of view.

Usually we have multiple layers in our system. It is common to have a web layer handling traffic from internet and a service layer handling the business logic and talking with the database.

Alt Text

Image created with https://excalidraw.com/

So who should check the permission? Web layer? Service layer? There is no standard answer.

Reviewing design is also important because you may start from a single team to multiple teams or system design is changed.

Discussion (3)

pic
Editor guide
Collapse
wparad profile image
Warren Parad

This is better known as AuthZ or Authorization (rather than authentication, what's the difference).

And realistically the solution is to always verify authorization at the service who owns the resource. In the case here that would be the service layer. If the web layer is really a composite service you still wouldn't make the check there. Only if it saved it own resource you could make check, and you should only be checking for the resource as it is owned/known by that service. Don't verify other services' permissions, those can change over time.

I know this is fairly complicated, which is exactly why services like Authress exist to solve this problem.

Collapse
phlash909 profile image
Phil Ashby

This particular security error also appears in the OWASP Top 10 as Broken Access Control (owasp.org/www-project-top-ten/2017...), along with recommendations on remediation such as: introducing a resource identifier mapping layer (typically generated at login and held in the web session) that only maps permitted resource keys (typically UUIDs) to true values (which now remain entirely server side); or as Warren notes above, delegating authorization checks to individual services (if that's how your server side works), that understand their specific context and how it relates to callers permissions (typically provided in a JWT).

Collapse
dgiulian profile image
Diego Giuliani

Great tip!
Another related one is to never use a sequential id but instead generate one using some library like uuid. This will make it harder for an attacker to guess the id of a user or any content in your app.