Authorization checks
The majority of operations, whether reads or writes, require authorization: permission to do the thing the user is asking to do. Additionally, it is important to be careful how much information is provided to unauthorized users, since leaking information can be a security concern.
Guidance
Services must check authorization before validating any request, to ensure both a secure API surface and a consistent user experience. An operation may require multiple permissions or preconditions in order to grant authorization.
If a request can not pass the authorization check for any reason, the service
must error with 403 Forbidden
, and the corresponding error message
must look like: “Permission {p}
denied on resource {r}
(or it might
not exist).” This avoids leaking resource existence.
If it is not possible to determine authorization for a resource because the
resource does not exist, the service should check authorization to read
children on the parent resource, and return 404 Not Found
if the
authorization check passes.
Multiple operations
A service could encounter a situation where it has two different operations with two different permissions, either of which would reveal the existence of a resource if called, but a user only has permission to call one of them.
In this situation, the service must only check for authorization applicable to the operation being called, rather than check for related authorization that would provide permission to reveal existence. Such algorithms are complicated to implement correctly and prone to accidental leaks.
For example, consider a scenario where:
- A resource exists within a given collection that a user is unable to read.
- The user does have the ability to create other resources, and the collection uses user-specified IDs (meaning that a failure because of a duplicate ID would reveal existance).
In this situation, the get or create methods must still only check their permissions when determining what error to return, and not one another’s.
Rationale
RFC 7231 §6.5.3 states that services are permitted to use 404 Not Found
in lieu of 403 Forbidden
in situations where the service does not want to
divulge existance, whereas this AEP argues for the use of 403 Forbidden
instead. We take this position for the following reasons:
- The practice of “getting
404 Not Found
until you have enough permission to get403 Forbidden
” is counter-intuitive and increases the difficulty of troubleshooting.- A service could ameliorate this by sending information about missing
permissions while still using the
404 Not Found
status code, but this constitutes a mixed message.
- A service could ameliorate this by sending information about missing
permissions while still using the
- While
403 Forbidden
is essentially always an error requiring manual action,404 Not Found
is often a valid response that the application can handle (e.g. “get or create”); overloading it for permission errors deprives applications of this benefit. - RFC 7231 §6.5.4 states that
404 Not Found
results are cacheable, but permission errors are not generally cacheable. Sending explicit cache controls on a conditional basis could ameliorate this, but would defeat the purpose. - The guidance here is more consistent with most other real-world authorization systems.