This section covers security requirements intended to secure your API both from unauthorized use and from authorized misuse. It also covers relevant, security-related topics such as CORS headers and browser security.
This is the multi-page printable view of this section. Click here to print.
Security and Compliance
- 1: Authorization
- 2: Do not use cookies
- 3: Cross-Origin Resource Sharing
- 4: Rate Limiting
- 5: Security Headers
1 - Authorization
For resources and actions that require authorization, the only permitted method is via Bearer Tokens as per RFC 7519. While the process of obtaining these tokens is outside the scope of this contract, they are likely issued by a security token service (STS) such as may exist in a federated OIDC or OAuth2 environment.
The structure and evaluation of this token is not up to us, though we strongly encourage you follow best practices such as OAuth2 Security Topics
POST /namespace/v1/resource HTTP/1.1
Authorization: Bearer <JWT Access Token>
JWT Token Payload
Assuming a token is properly decoded and validated, the following payload can be extracted and inspected for scopes and other non-normative permission markers.
{
"sub": "00000000-0000-0000-0000-000000000000",
"azp": "authorized_client_id",
"domain": "example.com",
"context": "00000000-0000-0000-0000-000000000000",
"iss": "https://issuer.example.com",
"exp": 1603075483,
"iat": 1603073683,
"jti": "00000000-0000-0000-0000-000000000000",
"scope": "openid profile email"
}
Considered Alternatives
Cookies
Cookies are problematic at best, and downright dangerous at worst. OWASP best practices recommend that cookies are explicitly domain bound, meaning that you are forced to a specific public api design, that of a single gateway to access all of your services. This is a contract, not a design document, and we want to avoid dictating your infrastructure.
Basic Auth
Basic Auth requires the use of explicit credentials for every api request, meaning that the resource owner’s username and password need to be known by every system that wishes to make a request.
API Key
API Keys are not a bad idea, however we strongly prefer that these are exchanged for bearer tokens with a limited TTL. This makes sure that API Keys can be quickly revoked, as they are only redeemed at a single service.
ID Tokens / Access Tokens
We do not explicitly define which access tokens may or may not be used, because I don’t know your use case. For modifying or reading a user’s own data, an ID token may be sufficient. For modifying or reading a different kind of resource, an access token may be more appropriate.
2 - Do not use cookies
But what about
No.
But there’s this library
No.
Well everyone else
No.
Why?
- They destroy privacy, requiring additional engineering work and support for GDPR and other privacy regulations.
- They are domain bound. To use them on multiple domains requires attaching them to the TLD, which starts to leak.
- They leak. Everywhere.
- Every last marketing tracker adds their own cookies to try to figure out who you are, and you have no control over what the browser does with those. A single visit to your company’s marketing site can add 20 cookies to your browser, and those will be attached to every single request to your API. Even if each of them only consumes 4KB of data, that’s 80KB of data that you’re sending on every request that you don’t need. It adds up quick.
- There’s always a new XSS attack.
- It makes you susceptible to CSRF attacks.
- There are size limits for how much data you can store in a cookie.
- There are limits to the number of cookies a browser will store per domain, which will conflict with the above leaking.
- You don’t need them.
In summary, even if you follow all of the OWASP recommendations for cookies, they are still a bad idea. Cookies create privacy concerns, security risks, performance impacts, technical debt, user experience overhead, and legal compliance concerns. There are better options available.
What should I use instead?
Please see our section on Authorization.
3 - Cross-Origin Resource Sharing
A proper implementation of the W3C’s Cross-Origin Resource Sharing CORS specification can greatly improve the utility of your API, and permits deployment decoupling between your clients and your UI. It can also cause quite a bit of hand-wringing by your security team, if not used correctly. The trick to know is that all CORS vulnerabilities stem from the use of Cookies for session management.
For the purposes of this contract, CORS support is required. While you can figure out the details of your implementation from the W3C specification, the following is a narrative overview of how the protocol works.
Preflight Request
For any Fetch or XMLHttpRequest that modifies a resource, the browser will first send a preflight request to the server
to ask if it is allowed to make this request. This will contain the Origin
header containing the domain name of the
requesting site, the http Method of the request in the Access-Control-Request-Method
header, and any non-simple
headers in the Access-Control-Request-Headers
header.
The server then responds by indicating which methods, headers, and domain is permitted. Note that if you do not
use Cookies, you can safely use the wildcard *
value for the Access-Control-Allow-Origin
header, which greatly
simplifies your implementation.
OPTIONS /namespace/v1/resources/{id} HTTP/1.1
Origin: https://api.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-Requested-With, Content-Type
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://api.example.com
Access-Control-Allow-Methods: PUT, GET, OPTIONS, DELETE
Access-Control-Allow-Headers: X-Requested-With, Content-Type
Access-Control-Max-Age: 86400
Main Request
For simple GET requests, or for other requests which have passed the preflight check, the client will only send
the Origin
header, which will contain the protocol, host, and port of the requesting site. The server will
successfully execute the request, however it will inform the browser whether it’s allowed to see that data by
setting the Access-Control-Allow-Origin
header.
POST /namespace/v1/resources/{id} HTTP/1.1
Origin: https://api.example.com
Content-Type: application/json
X-Requested-With: XMLHttpRequest
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://api.example.com
Vary: Accept-Encoding, Origin
Content-Type: application/json
4 - Rate Limiting
If your site requires some form of rate limiting, you must return a consistent
response when a client exceeds their quota. This response must use the 429
status code defined
in RFC 6585 Section 4, as well
as the Retry-After
header defined
in RFC 7231 Section 7.1.3.
The Retry-After
header should be set to the time at which the client can retry the request, using
the http-date type instead of the delta-seconds type.
For purpose of browser-level caching, the server may also include the Vary
header.
HTTP/1.1 429 Too Many Requests
Retry-After: Mon, 01 Jan 2018 00:00:00 GMT
Vary: Origin, Authorization
5 - Security Headers
The following headers prevent common web security vulnerabilities. In most cases they do not themselves require any additional logic, and can be attached to every single response coming from a resource server. Functionally, they let the server inform the browser client how to protect itself from common attacks.
Strict-Transport-Security: max-age=31536000; preload
This header tells the browser that your API must be accessed via TLS/HTTPS.
X-Content-Type-Options: nosniff
This header prevents a client from attempting to sniff - and override - the content-type sent by the server.
X-Frame-Options: DENY
This header prevents an API request from being loaded in an IFrame, which is a common XSS attack vector.
X-XSS-Protection: 1; mode=block
This is a legacy header for older browsers, which you may not support. Even so, it doesn’t hurt us to add it, and it provides similar protections to Content-Security-Policy.
Referrer-Policy: no-referrer
This header controls whether a browser, while on-site, sends the Referer header when linking to another site. This kind
of data is often used for user tracking, and resource servers rarely have any use for such. As such, the header returned
by any Resource Server should be no-referrer
.