DAST scanner · active probe

CORS Scanner

Cross-origin rules decide which websites may read your API with a visitor's cookies. The scanner sends three hostile Origin values to one endpoint per route group and checks whether the server agrees to them.

DASTMedium–CriticalCORS misconfiguration4 rules

What gets probed

CORS is a property of a route group, not of a single URL, so the scanner does not hit every endpoint. It picks one endpoint per path prefix, then sends that endpoint three different Origin values and reads what the server says back.

Discovered endpoints one pick per first two path segments /api/users/1api/users: picked /api/users/2api/users: seen /api/orders/9api/orders: picked /api/users/7/notesapi/users: seen /healthhealth: picked No cap: every distinct prefix is tested. Five endpoints, three prefixes, three picks. Origin probes, in order plain GET, no preflight 1 https://evil.comarbitrary_origin 2 nullnull_origin 3 https://{host}.evil.comsubdomain_bypass {host} is the target's own hostname. The first probe that yields a finding wins. Per endpoint /api/users/1 probe 1 echoed + ACAC: HIGH, stop probes 2 and 3 are never sent /api/orders/9 no ACAO header on any probe silent: the browser blocks it /health probe 1 got ACAO: * MEDIUM, stop

1Discovery produced many endpoints. The scanner keeps the first endpoint it sees for each distinct two-segment path prefix, so /api/users/1 stands in for everything under api/users.

2Each pick gets a GET with Origin: https://evil.com, then null, then https://<target-host>.evil.com. The third catches servers that match the origin by suffix or substring.

3The scanner stops at the first probe that produces a finding, so one endpoint yields at most one finding.

4No Access-Control-Allow-Origin in the response ends that probe silently. Without it the browser refuses to expose the response to another site, so there is nothing to report.

Reading the response

Two headers decide everything: Access-Control-Allow-Origin (ACAO) and Access-Control-Allow-Credentials (ACAC). The checks run in a fixed order and the first match wins.

ACAO in the response ACAC verdict confidence meaning *true CRITICAL0.95any site reads logged-in data equals the Origin sentany HIGH0.90echoes any caller · Medium 0.70 without ACAC nullany HIGH0.85sandbox reads it · Medium 0.70 without ACAC *not true MEDIUM0.70any site reads public responses https://app.example.comany silent-fixed value, or no header at all

1Wildcard plus credentials is CRITICAL, 0.95. ACAO: * together with ACAC: true is the worst case: any website can read authenticated responses.

2An echoed origin is HIGH, 0.90, when credentials are allowed. If ACAO equals the exact Origin the scanner sent, the server mirrors any caller. With ACAC: true the visitor's cookies ride along; without it only credential-less data is exposed, so it drops to MEDIUM, 0.70.

3ACAO: null is HIGH, 0.85, when credentials are allowed (MEDIUM, 0.70 otherwise). A page in a sandboxed iframe sends Origin: null, so allowing it lets an attacker reach the API from a sandbox. This rule is checked before the echoed-origin rule, so a null-only server gets this title.

4A wildcard without credentials is MEDIUM, 0.70. Any site can read the response, but not with the victim's cookies.

5Anything else is silent: a fixed allowed origin, or no header. A response with no ACAO header never reaches these rules at all.

What an allowlist changes

The fix is to echo an origin only when it is on a list you wrote. The scanner's probes then come back with no ACAO header, and the browser keeps the response from the attacker's page.

Origin: https://evil.comattacker's page or scanner probe not in ALLOWED_ORIGINSserver sets no CORS headers no ACAO headerbrowser blocks the read Origin: https://your-app.comthe real front end found in ALLOWED_ORIGINSserver echoes that one origin and ACAC ACAO: your-app.comread allowed The scanner sees the first case on all three probes: no ACAO, so no finding. Its probes never come from your-app.com, so the allowed case is never reported.

1An origin that is not on the allowlist gets no CORS headers at all. The browser then refuses to hand the response to the attacker's script.

2An origin on the list gets that single origin echoed back, with credentials allowed. Only that front end can read authenticated responses.

3This is why the scanner's three probes (evil.com, null, <host>.evil.com) all come back silent on a correct setup.

Why it matters

Why it matters

A CORS misconfiguration lets another website read your API as the logged-in user. If a user is signed in to your app and visits evil.com, evil.com's JavaScript can fetch your API and read account data, messages and tokens, and can also send state-changing requests from the victim's browser session.

Cryptocurrency exchanges (2016 to 2018)

Several exchanges were found reflecting arbitrary Origin headers in CORS responses. That allowed attacker-controlled websites to read authenticated API responses containing balances and API keys.

Vulnerable vs fixed code

// BAD: Wildcard CORS with credentials
response.headers.set("Access-Control-Allow-Origin", "*")
response.headers.set("Access-Control-Allow-Credentials", "true")

// BAD: Reflecting the Origin header without validation
const origin = request.headers.get("Origin")
response.headers.set("Access-Control-Allow-Origin", origin)

Limits and what it needs

RuleReports whenSeverity · confidence
Wildcard + credentialsACAO: * and ACAC: trueCritical · 0.95
Null origin (checked first)ACAO: nullHigh · 0.85 with ACAC: true, otherwise Medium · 0.70
Origin reflectedACAO equals the Origin sent (not *)High · 0.90 with ACAC: true, otherwise Medium · 0.70
Wildcard, no credentialsACAO: * without ACAC: trueMedium · 0.70
What it deliberately stays quiet about

The probes are plain GET requests, so a route that only sets CORS headers on an OPTIONS preflight is not seen. A response with no Access-Control-Allow-Origin is never reported, and only one endpoint per path prefix is tested, so a single route that differs from its siblings can be missed. Credentials are optional: with them, the probes carry the scan's auth headers.

The null-origin rule is evaluated before the echoed-origin rule, so a server that answers ACAO: null to the Origin: null probe is reported under the null-origin title.