RoastMyOpsec

Vulnerabilities

JSONP vs CORS for Public Browser APIs

July 17, 2024

JSONP vs CORS for public browser APIs is a legacy script tag versus Fetch CORS. JSONP wraps JSON in a caller-chosen function name so a <script src> can read another origin. That skips Access-Control-Allow-Origin entirely. Any page can load the URL. Cookies on a simple GET, secrets in the JSON, and a guessable callback name all become the other site's data. CORS lets you name origins, methods, and credentials on purpose. Wildcard plus cookies is still wrong — see credentials vs wildcard. RoastMyOpsec app mode sends a bounded untrusted Origin and reads ACAO. It does not execute JSONP callbacks, does not pad callback names, and does not steal sessions.

The practical difference

CORS is a browser-enforced read policy on XHR/fetch. JSONP is 'this URL is a JavaScript program.' Search engines and <script> tags do not honor ACAO. If the body is user data or an API key, JSONP is a publish.

A callback query parameter is the tell. So is application/javascript that starts with foo({"ok":true}). Content-Type JSON plus CORS is the replacement.

How to choose a replacement

Public, non-sensitive reads: CORS * or a static allowlist, no cookies. Logged-in browser clients: named origin, credentials only if you must, SameSite on the cookie. Server-to-server: no JSONP, no CORS — use keys on the server. Do not add JSONP 'for old IE.'

PatternOrigin controlCookie riskTakeaway
JSONP ?callback=None (script tag)Simple GETs may send cookiesRetire it
CORS named ACAOExplicit originOnly with credentials trueDefault for SPAs
CORS *Every origin may readIllegal with credentialsPublic non-secret only
Server proxyYour origin onlyYour session stays first-partyBest for secrets

What the roast can prove

A roast can show ACAO reflecting an untrusted Origin. It will not fetch ?callback=test or treat JSONP as a scored path. Hunt callback parameters in your own API docs and gateway. Pair with public JavaScript keys if the JSONP URL embeds a publishable token.

Common mistakes

The first mistake is leaving JSONP 'just for the marketing widget' on the same host as the account API.

The second mistake is reflecting the callback name without an allowlist of function identifiers — still retire JSONP rather than 'hardening' it.

The third mistake is adding CORS * and JSONP together and calling the stack modern.

Free audit the URL you own

RoastMyOpsec is a defensive public-surface roast: headers, cookies, sensitive paths, and more — no exploit payloads. Start with the free audit, then open the vault if the blurred findings look expensive.

Free audit now

FAQ

Is JSONP a vulnerability by itself?
It is a missing origin policy. If the payload is public weather, the risk is low. If it is user or account data, treat it as an exposure.
Can I lock JSONP to one site?
Not reliably. Script tags are not CORS. Move to CORS or a first-party proxy.
Does RoastMyOpsec detect JSONP?
No. It does not execute callbacks. Check CORS headers and remove callback routes in your own review.

Sources

Related guides