OWASP Broken Access Control by example: preventing user’s from reading/writing data that isn’t theirs

  softwareengineering

I have experience building RBAC-based authorization mechanisms, and understand the theory behind ACLs (DAC?) though I’ve never had the need to implement them.

A situation was just presented to me that I realize I have never thought about dealing with before, but absolutely should have. I believe OWASP refers to this problem as Broken Access Control, but the scenario is this:

User X should not be allowed to read/write certain data belonging to User Y.

So for instance, User X is a valid, authenticated user/principal in my system; and so is User Y.

Each user in the system has a “public profile” which consists of, say, 20+ tidbits of information about them that is safe to be shared with all other authenticated users. Things like: username, last login time, length of time they’ve been a user, their favorite food, etc.

However, certain information, such as their email, payment info, password, and perhaps other sensitive information should only be visible to the user for whom the information belongs to, and only on the appropriate screens, when they are logged in. Furthermore, no one other than them should ever be able to write new values for those pieces of data. So User X should never be able to update User Y’s email or password, etc.

Let’s say I have RESTful endpoints that allow users to fetch + update their data. So you might have:

  • GET /users/{userId}/profile : returns a JSON object with all their shareable “public” data
  • PUT /users/{userId}/email : updates a user’s email address with a new value

In the latter case, we need to restrict access to the endpoint in such a way that the requester can only be permitted to take the action if its for their own userId.

I’m wondering what the typical solution is for such a scenario?

Assume a JWT-based authentication mechanism where the user logs in, the backend generates a JWT for them, and they can then make calls to authenticated URLs using those JWTs. The JWTs can contain whatever claims they need in order to make the solution work!

1

Let’s ignore JWT tokens for the moment and think of a classical session based authentication mechanism using cookies:

  • a user accesses a login page;
  • they use their username and password to login;
  • server checks the database and finds the username+password match;
  • the user data is stored in the session and the user receives a SESSION_ID cookie;
  • any further requests made by the user will contain the SESSION_ID;
  • the server identifies the session based on the SESSION_ID;
  • the session at the server contains the user data;
  • you use the user data in the session to authorize the user for access to any of the data within the application;

Let’s consider an example:

  • I login as Bogdan with my password. I’m just a normal user with no specific roles.
  • I get back a SESSION_ID;
  • based on this SESSION_ID, all further requests I make are identified by the server and the application as coming from me, Bogdan.
  • now, I access the /admin URL. As I said, I do not have any specific roles. Certainly not the admin role.
  • the application code checks to see if Bogdan is an admin. Nope!
  • the application generates a HTTP 403 Forbidden response and sends it back;
  • Bogdan does not get access to the /admin URL.
  • the end.

When you introduce JWT tokens, this mechanism doesn’t change, only implementation details differ. Instead of having a SESSION_ID cookie to identify the user at the server and in the application, you now have a token that contains all the data needed to verify the validity of the token and identify the user within it. Your application still needs to check if the user in the token has access to certain resources or not.

Using your example of PUT /users/{userId}/email, once you identify the client at the server, the application needs to check if the identified user matches the {userId} in the URL. No? Then 403 Forbidden. In fact, you don’t even need to check the URL. You don’t authorize access to the URL, you authorize access to the data the user wants to change. If the URL is /users/123/email or /users/me or /blabla, it doesn’t really matter. That’s just an entry point for changing an email address. And obviously, the application should prevent some user from changing someone else’s email (unless they are an admin, or support user, or whatever role your application allows for changing other emails).

So once you get your JWT token at the server, you validate it (not expired, signature valid, etc), get the details of the user from the token, and use those to compare against roles in your application. Not sure what technology you use but there are libraries, toolkits, or frameworks that can help with that, or you can even do it manually (although a bit of a pain). I found what looks like a decent JS example here, if you want to find out more.

LEAVE A COMMENT