Add the cors-csrf module
This commit is contained in:
79
cors-csrf/docs/03-three-identical-403s.md
Normal file
79
cors-csrf/docs/03-three-identical-403s.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# 3. The three identical 403s
|
||||
|
||||
*Prev: [2. Who resolves the CorsConfigurationSource](02-who-resolves-the-source.md) · Next: [4. Preflight handlers](04-preflight-handlers.md)*
|
||||
|
||||
`DefaultCorsProcessor` runs three checks on a preflight, in order: origin, method, request
|
||||
headers. All three failures produce the same thing.
|
||||
|
||||
```
|
||||
HTTP/1.1 403
|
||||
Vary: Origin
|
||||
Vary: Access-Control-Request-Method
|
||||
Vary: Access-Control-Request-Headers
|
||||
|
||||
Invalid CORS request
|
||||
```
|
||||
|
||||
Same status, same body, byte for byte, no `Access-Control-*` header to distinguish them. The
|
||||
assertion in `CorsContractTests.threeRejectionsLookIdentical` compares the three bodies for
|
||||
equality, so if a future version starts distinguishing them, that test fails.
|
||||
|
||||
The only place the difference exists is a DEBUG log line, and the three are worth memorising
|
||||
because they are the fastest CORS diagnosis available:
|
||||
|
||||
```
|
||||
o.s.web.cors.DefaultCorsProcessor : Reject: 'https://evil.example.com' origin is not allowed
|
||||
o.s.web.cors.DefaultCorsProcessor : Reject: HTTP 'DELETE' is not allowed
|
||||
o.s.web.cors.DefaultCorsProcessor : Reject: headers '[authorization]' are not allowed
|
||||
```
|
||||
|
||||
Turn them on with:
|
||||
|
||||
```yaml
|
||||
logging.level.org.springframework.web.cors: DEBUG
|
||||
```
|
||||
|
||||
The complete set of messages, read out of the class's constant pool, is five:
|
||||
|
||||
| Message | Meaning |
|
||||
|---|---|
|
||||
| `Skip: no CORS configuration has been provided` | The source returned `null` for this path — chapter 2 |
|
||||
| `Skip: response already contains "Access-Control-Allow-Origin"` | Something upstream already handled it |
|
||||
| `Reject: origin is malformed` | The `Origin` header did not parse |
|
||||
| `Reject: '…' origin is not allowed` | |
|
||||
| `Reject: HTTP '…' is not allowed` | |
|
||||
| `Reject: headers '[…]' are not allowed` | |
|
||||
|
||||
## Status codes, and what each one means
|
||||
|
||||
Collecting the states this module reproduces:
|
||||
|
||||
| What you see | What it means |
|
||||
|---|---|
|
||||
| `401`/`403`, no `Access-Control-*` at all | No `CorsFilter` in the chain. The preflight was judged by authorization — chapter 1 |
|
||||
| `200`, no `Access-Control-*` | `CorsFilter` is present and found no configuration for this path — chapter 2 |
|
||||
| `403`, `Invalid CORS request` | `CorsFilter` is present and rejected origin, method or headers — this chapter |
|
||||
| `200` with `Access-Control-Allow-Origin` | It worked |
|
||||
| `404`, no `Access-Control-*` | The path is outside the pattern you registered. Common with `/api/**` versus a mis-typed URL |
|
||||
|
||||
The browser reports the same "blocked by CORS policy" for the first four rows. Two of them are
|
||||
not CORS problems.
|
||||
|
||||
## What a **simple** request does
|
||||
|
||||
Only preflighted requests get intercepted. A simple `GET` runs the whole chain, so an
|
||||
unauthenticated one returns 401 — **carrying** the CORS header, because `CorsFilter` at
|
||||
1000 already wrote it before `AuthorizationFilter` at 4200 rejected the request:
|
||||
|
||||
```
|
||||
HTTP/1.1 401
|
||||
Access-Control-Allow-Origin: https://spa.example.com
|
||||
Access-Control-Allow-Credentials: true
|
||||
```
|
||||
|
||||
That is the good case: the SPA's `fetch` resolves and the code can read `response.status`. It is
|
||||
also the reason "my POST fails but my GET returns a readable 401" is a coherent bug report and
|
||||
not a contradiction.
|
||||
|
||||
---
|
||||
*Prev: [2. Who resolves the CorsConfigurationSource](02-who-resolves-the-source.md) · Next: [4. Preflight handlers](04-preflight-handlers.md)*
|
||||
Reference in New Issue
Block a user