Discretion ≠ security through obscurity

Sometimes, when writing web services, I have the temptation to reveal too much information. Let’s consider two scenarios.

Scenario 1: 404 > 401

Suppose an unauthorized user asks for a resource:

GET /users/DalaiLama HTTP/1.1

In the privileged, omniscient universe of the API author, I am tempted to tell the truth. The whole truth is that the user is just plain not allowed to see this resource.

HTTP/1.1 401 Unauthorized
Content-Length: 52
Content-Type: application/json

{"success": false, "message": "unauthorized access"}

But perhaps, I shouldn’t be describing the world as it is. Rather, I should be describing the world as it ought to be. In an ideal world, a client would never ask for something she doesn’t have authorization to see. The only requests for resources she should not access would be either accidents or resources that no longer exist.

However, it may be that we don’t live in an ideal world. If a client is maliciously trying to suss out as much information as she can, it’s better to simply report that the requested resource was not found:

HTTP/1.1 404 Not Found
Content-Length: 51
Content-Type: application/json

{"success": false, "message": "resource not found"}

Scenario 2: bad credentials

Suppose your system requires a user name and a password for authentication. Typically, there are two ways to fail authentication: by supplying a user name that does not exist or by providing an incorrect password. There are other edge cases, but those are the two primary ways of failing to authenticate. As an API author, again with privileged access to all the data inside the system, I could helpfully report back to the user:

HTTP/1.1 401 Unauthorized
Content-Length: 56
Content-Type: application/json

{"success": false, "message": "user name was not found"}

Again, though, it is safer to provide a less specific message, something more akin to this:

HTTP/1.1 401 Unauthorized
Content-Length: 48
Content-Type: application/json

{"success": false, "message": "bad credentials"}

Any legitimate user will know enough to try a different set of credentials. It’s true that she may need to try more combinations, but the alternative is that your login endpoint becomes a reliable method of polling the system to see if an arbitrary user name exists.

Conclusion

These recommendations are not earth-shattering. Every API author needs to assess the correct position to take on the spectrum of helpfulness vs. discretion when it comes to returning error responses. Depending on the use case (e.g., the expected user base is trusted users behind a firewall), an API may reasonably offer extremely detailed errors or choose to be more discreet.

Posted by Afshin T. Darian