Choosing the right error code 401, 403, or 404
By: Warren Parad
Tags: Application Security, Rest Api, Api Security, Software Development, Authorization

Selecting the right response to API requests helps secure your application. While it may not seem so on the outside, every unnecessary piece of information makes it easier for an attacker to understand how to gain access. And on the flip side every piece of missing information makes it harder for a consumer of your API to understand the response to an HTTP request.

Here we’ll break down the most common HTTP error responses used for the purposes of API security. When a request is successful, that means that:

The request token uniquely identifies a user correctly

The resource in the request exists

The action on the resource is valid

The user has necessary permission for that action on that resource

Then a successful response status code can be used => 2XX

Fundamentally there are three relevant error codes: 401, 403, 404.

401

401 — indicates that the request has not been applied because it lacks valid authentication credentials for the target resource. The user is not authenticated. (See here for more information on the difference between authentication and authorization). The API requires a valid user, this is determined by the Authorization header in the request. A 401 is the right error code when:

There is no token specified

The token specified is in an invalid format

The token has expired as token contain a window for when they are valid

And in rare cases, the token is valid, but should not be used for this API (also known as the audience)

403

403 — indicates that the server understood the request but refuses to authorize it. The user is not authorized. The user attempted to perform an action, but the token that identified the user does not have sufficient permissions to do that. It’s really helpful to return the permissions that the user is missing so that they can go request them from an admin. Google presents to you a page like this:

404

404 — indicates that the server can’t find the requested resource. Links which lead to a 404 page are often called broken or dead links, and can be subject to link rot. If the url path otherwise known as the resource doesn’t exist, then a 404 is appropriate.

Picking the right error code

That seems easy to handle, and most of the time, they go in order:

Validate Token Verify user permissions Check resource existence

Sometimes 2 and 3 happen in the reverse order depending on what’s easier for the application server. However, when you do this you could be opening your resources up to exposing too much information. What happens if a user doesn’t have access to a resource and that resource exists? 403 seems reasonable. What happens if that resource doesn’t exist? 404?

In that case, without access to a resource, you return different information based on whether that resource exists. A user without permission can start scanning all your endpoints and potential resources searching for existing ones. If those resources could be publicly shareable like Zoom sessions, you could end up with more Zoombombing on your hands.

Instead we should break down 403’s into more nuanced categories. If a user knows about a resource and doesn’t have permission then return a 403. You may still want to share the missing permissions or two to request access from. However, if the user shouldn’t know about the resource, then neither return who to contact, the missing permissions, nor a 403 suggesting that the resource exists. In this case return the 404.

A concrete example is what Authress provides when authorizing users. Your application has resources. Access to those resources is stored in Authress access records. Access records are account specific. If an Authress user from another access asks to read one of your accounts access records, they get a 404. If they ask to see the account info, 404. If they attempt to get a list of users with access to a resource, 404. Under no circumstance do they get any other than a 404, except if they:

Make a request to a list resource. They get an empty array ( [] ) and a 200. (collection endpoints don't return 404)

) and a 200. (collection endpoints don't return 404) Have knowledge of the domain your account is using, then they can successfully access the identity provider configuration.

And that’s about it.

Returning a 403 is great for your actual users, but can start to expose important details of your application. Don’t let potential attackers know which resources exist.

Going further

Want to see how Authress uses error codes to ensure security? Checkout the Authress Management Portal — API section.