Rules Hub
Coding Rules Library
Rule priority, scope & exceptions
Use this to align rules with the senior-level structure (P0/P1/P2, scope, exceptions/tradeoffs).
Use a strict CORS origin allowlist; never combine wildcard/reflected origin with credentials
Restrict CORS to a known allowlist of origins. Never reflect the request Origin unchecked, and never send Access-Control-Allow-Credentials:true alongside Access-Control-Allow-Origin:*.
Bad example
| 1 | import cors from 'cors'; |
| 2 |
|
| 3 | // Reflects ANY origin and allows credentials -> any site can call |
| 4 | // your authenticated API with the victim's cookies. |
| 5 | app.use( |
| 6 | cors({ |
| 7 | origin: (origin, cb) => cb(null, true), // reflects request origin |
| 8 | credentials: true, |
| 9 | }), |
| 10 | ); |
| 11 |
|
| 12 | // Equally broken, done by hand: |
| 13 | res.setHeader('Access-Control-Allow-Origin', req.headers.origin ?? '*'); |
| 14 | res.setHeader('Access-Control-Allow-Credentials', 'true'); |
Explanation (EN)
Reflecting the Origin header back (or origin: true) means the browser treats every site as an allowed origin. With credentials:true, an attacker's page can issue authenticated cross-origin requests using the victim's cookies and read the responses. The hand-rolled version with '*' plus credentials is rejected by browsers but reveals the same flawed intent — and the reflected-origin fallback is exploitable.
Objašnjenje (HR)
Reflektiranje Origin headera (ili origin: true) znaci da preglednik tretira svaku stranicu kao dopusten izvor. Uz credentials:true, napadaceva stranica moze slati autenticirane cross-origin zahtjeve s zrtvinim kolacicima i citati odgovore. Rucna varijanta s '*' i credentials preglednici odbijaju, ali otkriva istu pogresnu namjeru — a fallback na reflektirani origin je iskoristiv.
Good example
| 1 | import cors from 'cors'; |
| 2 |
|
| 3 | const ALLOWED_ORIGINS = new Set([ |
| 4 | 'https://app.example.com', |
| 5 | 'https://admin.example.com', |
| 6 | ]); |
| 7 |
|
| 8 | app.use( |
| 9 | cors({ |
| 10 | origin: (origin, cb) => { |
| 11 | // allow same-origin / server-to-server (no Origin header) |
| 12 | if (!origin || ALLOWED_ORIGINS.has(origin)) return cb(null, true); |
| 13 | return cb(new Error('Origin not allowed by CORS')); |
| 14 | }, |
| 15 | credentials: true, |
| 16 | methods: ['GET', 'POST', 'PUT', 'DELETE'], |
| 17 | maxAge: 600, |
| 18 | }), |
| 19 | ); |
Explanation (EN)
The allowlist is an exact-match Set, so only known front-ends can make credentialed requests; everything else is rejected at the preflight. When you echo a matched origin you must also send Vary: Origin (the cors package does this) so caches don't serve one origin's ACAO header to another.
Objašnjenje (HR)
Allowlista je Set s tocnim podudaranjem pa samo poznati frontendi mogu slati zahtjeve s vjerodajnicama; sve ostalo se odbija na preflightu. Kad vracas podudareni origin, moras poslati i Vary: Origin (cors paket to radi) kako cache ne bi posluzio ACAO header jednog origina drugome.
Notes (EN)
OWASP CORS Cheat Sheet. Validate the Origin allowlist server-side; CORS is a browser-enforced control, not server authorization — keep auth checks regardless.
Bilješke (HR)
OWASP CORS Cheat Sheet. Provjeravaj allowlistu origina na serveru; CORS je kontrola koju namece preglednik, ne serverska autorizacija — zadrzi provjere autentikacije neovisno o tome.
Exceptions / Tradeoffs (EN)
Truly public, credential-free APIs (no cookies, no Authorization) may use Access-Control-Allow-Origin: * — but then credentials MUST be omitted/false. Do not use substring/regex matching like origin.endsWith('example.com') which 'evil-example.com' or 'example.com.attacker.io' can defeat; match full origins exactly.
Iznimke / Tradeoffi (HR)
Stvarno javni API-ji bez vjerodajnica (bez kolacica, bez Authorization) smiju koristiti Access-Control-Allow-Origin: * — ali tada credentials MORA biti izostavljen/false. Ne koristi substring/regex podudaranje poput origin.endsWith('example.com') jer ga 'evil-example.com' ili 'example.com.attacker.io' moze zaobici; podudaraj cijele origine tocno.