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:
Then a successful response status code can be used => 2XX
Fundamentally there are three relevant error codes: 401, 403, 404.
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:
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 - 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.
That seems easy to handle, and most of the time, they go in order:
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:
) and a 200. (collection endpoints don’t return 404)
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.
Want to see how Authress uses error codes to ensure security? Checkout the Authress Management Portal - API section.