Web Development – Should MVC/REST Return 403 or 404 for Unauthorized Resources?

privacySecurityweb servicesweb-applicationsweb-development

When working with a resource-based site (such as an MVC application or REST service), we have two main options when a client tries to GET a resource that they don't have access to:

  • 403, which says that the client is unauthorized; or
  • 404, which says that the resource does not exist (or couldn't be located).

Common wisdom and common practice seems to be to respond with the truth – that is, a 403. But I'm wondering if this is actually the right thing to do.

Secure login systems never tell you the reason for a login failure. That is to say, as far as the client is concerned, there is no detectable difference between a non-existent user name and an incorrect password. The purpose is of this is to not make user IDs – or worse, e-mail addresses – discoverable.

From a privacy standpoint, it seems a lot safer to return a 404. I'm reminded of the incident wherein someone reportedly found out the winners of a reality show (Survivor, I think) by looking at which resources didn't exist on the site vs. which ones did. I'm concerned about a 403 potentially giving away sensitive information like a serial number or account number.

Are there compelling reasons not to return a 404? Could a 404 policy have negative side effects elsewhere? If not, then why isn't the practice more common?

Best Answer

There's a general misconception (and misuse) associated with 403 Forbidden: it's not supposed to give anything away about what the server thinks about the request. It's specifically designed to say,

I get what you're requesting, but I'm not going handle the request, no matter what you try. So stop trying.

Any UA or client should interpret that to mean that the request will never work, and respond appropriately.

This has implications for clients handling requests on behalf of users: if a user isn't logged in, or mistypes, the client handling the request should reply, "I'm sorry, but I can't do anything" after the first time it gets the 403 and stop handling future requests. Obviously, if you want a user to still be able to request access to their personal information after a failure, this is a user-hostile behavior.

403 is in contrast to 401 Authorization Required, which does give away that the server will handle the request as long as you pass the correct credentials. This is usually what people think about when they hear 403.

It's also in contrast with 404 Page Not Found which, as others pointed out, is designed not only to say "I can't find that page" but to suggest to the client that the server makes no claims of success or failure for future requests.

With 401 and 404, the server doesn't say anything to the client or UA about how they should proceed: they can keep trying in hopes of getting a different response.

So 404 is the appropriate way to handle a page you don't want to show to everyone, but don't want to give away anything about why you won't show it in certain situations.

Of course, this assumes the client making the request cares for petty RFC flippancy. A malicious enough client isn't going to care about the status code returned except in an incidental manner. One will know it's a hidden user page (or a potential hidden user page) by comparing it to other, known user pages.

That is, let's say your handler is users/*. If I know users/foo, users/bar and users/baaz work, the server returning a 401, 403, or 404 for users/quux doesn't mean I'm not going to try it, especially if I have reason to believe there is a quux user. A standard example scenario is Facebook: my profile is private, but my comments on public profiles are not. A malicious client knows I exist even if you return 404 on my profile page.

So status codes aren't for the malicious use cases, they're for the clients playing by the rules. And for those clients, a 401 or a 404 request is most appropriate.

Related Topic