Skip to content

Security

Cors Misconfiguration and false confidence from a quick glance

A quiet SPA network panel is not a CORS review. False confidence shows up when teams only test their own origin while public responses still allow untrusted sites to read them.

· by Sam Ortega

Cors Misconfiguration and false confidence from a quick glance

Uptime was green. The SPA loaded. Someone glanced at Network, saw 200 from api.example.com, and wrote "CORS OK" in the handoff doc. Then a security questionnaire asked whether untrusted sites can read API responses. Silence. That silence is the topic.

SlaySlop's CORS misconfiguration check "Checks whether cross-origin resource sharing allows untrusted sites to read responses." A quick glance at your own front end never asks that question. The check does.

Outcome you want instead of a glance

A short, boring paragraph: which Origins are allowed, on which routes, with or without credentials, verified on the public host. Process that survives launch week looks like that paragraph plus a rescan date—not like a checkbox typed from memory on a call.

What the glance gets wrong

The glance watches the happy path. Intended Origin. Intended route. Intended cookies already in the profile. It never sends Origin: https://untrusted.example. It never reads ACAO next to Allow-Credentials. It confuses "browser blocked my mistaken local file Origin" with "API is locked down."

Loading diagram.

bash
# Permissioned contrast: intended vs untrusted
API="https://api.example.com/v1/me"
for origin in "https://app.example.com" "https://untrusted.example"; do
  printf '%s => ' "$origin"
  curl -sI -H "Origin: $origin" "$API" \
    | awk 'BEGIN{IGNORECASE=1} /^access-control-allow-origin:/{print; found=1} END{if(!found) print "(no ACAO)"}'
done

If both lines show the same reflected Origin value—or the untrusted line shows a grant you cannot explain—your glance was theater.

Boundaries that keep the call honest

SlaySlop is read-only. Permission required. Not a pentest. Findings include the evidence that triggered them. The scanner maps public exposure; it does not bless every private subnet. CORS is one Security check beside CSP ("Checks whether browser content sources are restricted by a CSP header") and frame protection ("Looks for controls that prevent unwanted framing and clickjacking").

Do not tell a client the Security category is done because CORS looked quiet on the marketing site alone. Name the API host in the portal. Name what you did not scan.

Happy path against false confidence

  1. List public app and API hosts from the SOW.
  2. Probe intended and untrusted Origins on a sensitive route.
  3. Read CORS misconfiguration evidence in SlaySlop for those hosts.
  4. Skim CSP and frame protection on the same day.
  5. Write the allowlist into the handoff notes where the next person will trip over it.

Agencies that skip step 5 relearn the same incident after staff rotation. I have been that next person. It is not fun.

Pitfalls

CDN config in one environment, origin config in another. You glance at staging headers. Production still reflects. Another pitfall: temporary * for a partner demo that somehow reached production Friday night. Temporary is not a control. Expiry dates in tickets are a control.

Also: equating "no CORS finding" with "no sensitive data on the public internet." CORS constrains browser script reads across origins. It is not authentication. It is not a substitute for authz on /v1/me. Keep those conversations separate so the client does not walk away with the wrong calm.

A 10-minute anti-glance ritual

Before any Security handoff call, spend ten minutes you cannot bill to vibes:

  1. Open the API host from the SOW.
  2. Send intended and untrusted Origins.
  3. Open the SlaySlop CORS row.
  4. Say the allowlist out loud once.

If you cannot say the allowlist out loud, you are not ready for the call. That ritual has saved me from more embarrassment than any fancy gateway dashboard.

False confidence from partner demos

Partner demos are where temporary Origins go to become permanent. Someone adds https://demo-partner.example on Thursday for a sales call. Nobody removes it. Three months later the partner subdomain is gone and the allowlist entry remains—or worse, the entry was actually a broad reflection rule dressed up as a partner exception.

I ask for the removal ticket id in the same message that approves the temporary Origin. If there is no ticket id, there is no temporary Origin. That sounds petty. It is cheaper than explaining an old grant on a renewal call.

When marketing and API hosts differ, put both hostnames in the calendar invite for the handoff call. People glance at what is in front of them. Give them the right front.

Related next step

If the glance and the finding disagree, trust the evidence, reproduce once, then fix the allowlist. Soft product mention: SlaySlop can keep scanning the public host after handoff so a temporary grant does not linger unnoticed. Glossary: CORS misconfiguration.