TL;DR: Don't leak information through HTTP error codes.
Here is a possible vulnerability that is probably not very serious but easy to overlook. ...
For further actions, you may consider blocking this person and/or reporting abuse
Maybe "more RESTFully" logic may be:
return 401 if user unauthenticated (it's does not matter exists account or not);
return 403 if user authenticated but is not owner of requested account (it's does not matter exists account or not);
return 404... I think never :)
That has the exact same problem as 401 / 404, it just requires the the additional step of registering. There is little to no security gain.
If there is no authentication in API, and we want to protect our growth rate statistics - yes, same problem exists. And GUID instead of INT identifiers may help.
Even if there is authentication in API, it doesn't matter. (See Panera's "fix")
GUID or other unpredictable identifier is the only real fix. Rate limiting each user can help as well, but how useful that is really depends on how hard it is to get authentication tokens.
That certainly makes sense.
Agreed, also don't use internal db ids in the api so the attacker cannot infer the sequencing
If your external ids are unguessable (e.g., 256-bit random strings) then this attack completely disappears. Another alternative is to only expose
/api/accounts/me
if there is no valid reason for a user to ever access any other account.Yep.It's always good to use a random string or a different Identifier for any public resource.
Not to say this is not important, but there is more to it if you want to plug the informational leaks in a serious way.
Using the same example as in the OP, let's say example.com/api/accounts/300 and 301 both return the same HTTP code. Another thing to think about then is timing -- let's say an attacker can do a thousand requests for each of the accounts.
If they find that on average accounts 301, 302 and 304 take 10ms, but 300, 303 and 305 take 8.5ms -- then they have found that there's a difference. Perhaps the backend code is written so that if you find that the account exists, you do a credentials check; but if the account doesn't exist you skip the credentials check. Then an attacker can still know whether the account exists or not, given the timings.
Btw this is also the reason why checking an auth token with == is not a valid practice -- you have to use a "time-secure" comparison
I wrote an article about building better web APIs and I pointed out that I use a similar approach: return 404 not only when the resource does not exist, but also when it does exist and the authenticated user doesn't own it.