Skip to main content

Command Palette

Search for a command to run...

JSONP Explained: How It Works and Why It Matters for Web Security

Updated
•11 min read•View as Markdown
S
I like breaking things that are supposed to be secure. When I’m not hunting vulnerabilities, I’m exploring systems, architectures, and the assumptions behind them.

If you work in application security, you've probably encountered JSONP while studying the Same-Origin Policy (SOP), cross-origin attacks, or legacy web applications.

At first glance, JSONP looks simple:

JSONP = JSON wrapped inside a JavaScript callback.

But to understand its security implications properly, you need to understand what the browser actually does with the response.

This article breaks down JSONP from the browser's perspective and explains why it worked as a cross-origin data-sharing mechanism, how it differs from CORS, and where the security risks come from.


1. Start With the Same-Origin Policy

Let's say your application is hosted at:

https://www.example.com

and the API is hosted at:

https://api.example.com

These are different origins because the hostname is different.

If JavaScript on www.example.com performs:

fetch("https://api.example.com/user");

the browser may make the request, but JavaScript cannot freely read the response unless the API explicitly permits the requesting origin through CORS.

Conceptually:

www.example.com
       |
       | fetch()
       v
api.example.com
       |
       | response
       v
    Browser
       |
       X
       |
JavaScript cannot read
the response unless
CORS permits it

This is an important distinction:

The Same-Origin Policy does not prevent all cross-origin requests. It restricts what the requesting origin is allowed to read or do with the response.


2. The <script> Tag Is Special

Browsers have historically allowed web pages to load JavaScript from other origins.

For example:

<script src="https://cdn.example.com/app.js"></script>

Your application might be:

https://www.example.com

while the JavaScript is hosted at:

https://cdn.example.com

The browser downloads the script and executes it.

So the browser has a mechanism that effectively allows:

www.example.com
       |
       | <script src="...">
       v
cdn.example.com

This behavior is the primitive that JSONP takes advantage of.


3. What Happens If We Put JSON Directly in a <script>?

Suppose an API returns:

{
  "id": 123,
  "name": "Amar"
}

Now imagine requesting it using:

<script src="https://api.example.com/user"></script>

The browser expects the response to be JavaScript.

But the server returned JSON:

{
  "id": 123,
  "name": "Amar"
}

That's not the JSONP pattern we want.

So JSONP changes the response.

Instead of returning:

{
  "id": 123,
  "name": "Amar"
}

the server returns:

callback({
    "id": 123,
    "name": "Amar"
});

Now the response is valid JavaScript.

That's the core idea behind JSONP.


4. The Callback Is the Key

Suppose the client defines this function:

function handleUser(data) {
    console.log(data.name);
}

The client then loads:

<script src="https://api.example.com/user?callback=handleUser"></script>

The server sees:

callback=handleUser

and generates:

handleUser({
    "id": 123,
    "name": "Amar"
});

The browser receives the response and executes it.

The flow is:

Browser
   |
   | GET /user?callback=handleUser
   |
   v
API Server
   |
   | Generates JavaScript
   |
   | handleUser({...})
   v
Browser JavaScript Engine
   |
   | Executes
   v
handleUser(data)

The JSON data is now available to the page's JavaScript.


5. A Complete JSONP Example

Client

<script>
function receiveUser(data) {
    console.log("User ID:", data.id);
    console.log("User name:", data.name);
}
</script>

<script src="https://api.example.com/user?callback=receiveUser"></script>

HTTP Request

GET /user?callback=receiveUser HTTP/1.1
Host: api.example.com

Server Response

HTTP/1.1 200 OK
Content-Type: application/javascript

receiveUser({
    "id": 123,
    "name": "Amar"
});

Browser

The browser executes:

receiveUser({
    id: 123,
    name: "Amar"
});

Therefore:

data.name

is available inside receiveUser().


6. Why Can JavaScript Access the Data?

This is the most important concept to understand.

The browser isn't saying:

"This is a cross-origin JSON response, so I'll allow JavaScript to read it."

Instead, the browser sees:

<script src="https://api.example.com/..."></script>

and treats the response as a script resource.

The response is therefore executed as JavaScript.

So JSONP is better understood as:

Cross-origin script inclusion where the server converts the requested data into executable JavaScript.

This distinction is extremely important from a security perspective.

JSONP isn't simply:

"Cross-origin JSON reading"

It is:

"Cross-origin JavaScript execution containing JSON data"

7. Does JSONP Actually Bypass the Same-Origin Policy?

It's better to say that JSONP takes advantage of a browser feature that is permitted by the Same-Origin Policy model.

Browsers intentionally allow certain cross-origin mechanisms.

For example:

Mechanism Cross-origin behavior
<img> Can load cross-origin resources
<script> Can load and execute cross-origin scripts
<link> Can load certain cross-origin resources
HTML forms Can submit cross-origin requests
fetch() Cross-origin response access is controlled by CORS
XHR Cross-origin response access is controlled by CORS

So the misconception to avoid is:

"SOP blocks all cross-origin communication."

It doesn't.

The browser has always permitted certain forms of cross-origin interaction.

JSONP uses the <script> mechanism.


8. JSONP Request Flow

The complete flow looks like this:

                JSONP FLOW

┌──────────────────────┐
│      Browser         │
│                      │
│ function foo(data)   │
└──────────┬───────────┘
           │
           │ <script src=
           │ https://api.example.com/
           │ user?callback=foo>
           │
           ▼
┌──────────────────────┐
│      API Server      │
│                      │
│ Reads callback=foo   │
└──────────┬───────────┘
           │
           │ Generates
           │ JavaScript
           │
           ▼
┌──────────────────────┐
│ HTTP Response        │
│                      │
│ foo({                │
│   "id": 123,         │
│   "name": "Amar"     │
│ })                   │
└──────────┬───────────┘
           │
           │ Browser executes
           ▼
┌──────────────────────┐
│ foo(data)            │
│                      │
│ JavaScript receives  │
│ the JSON data        │
└──────────────────────┘

9. Why JSONP Was Useful

Before modern CORS support became the standard way to allow controlled cross-origin API access, developers needed ways for applications hosted on different domains to exchange data.

JSONP provided a relatively simple solution:

Application
    |
    | <script>
    v
Remote API
    |
    | JavaScript response
    v
Application

It worked particularly well for public APIs and simple GET-based data retrieval.

However, it came with significant limitations and security risks.


10. JSONP Is GET-Based

Because JSONP relies on:

<script src="..."></script>

the request is essentially a GET request.

JSONP therefore isn't appropriate for normal API operations such as:

POST
PUT
PATCH
DELETE

This is another reason modern applications use CORS and standard HTTP APIs instead.


11. The Major Security Problem: JSONP Executes Code

With a normal JSON API:

{
  "name": "Amar"
}

the response is data.

With JSONP:

callback({
    "name": "Amar"
});

the response is executable JavaScript.

That changes the security model considerably.

The server is effectively telling the browser:

"Execute this JavaScript."

This is why JSONP endpoints need to be treated carefully.


12. Potential Cross-Origin Data Disclosure

Imagine a sensitive API:

https://bank.example/api/account?callback=foo

which returns:

foo({
    "balance": 500000,
    "accountNumber": "..."
});

An attacker might attempt to embed:

<script src="https://bank.example/api/account?callback=foo"></script>

If the victim's browser sends the necessary authentication credentials and the endpoint is vulnerable to this type of cross-origin access, the returned data could become available to the attacker's page.

Conceptually:

Victim Browser
      |
      | Authenticated request
      v
Bank API
      |
      | foo(sensitive data)
      v
Attacker-controlled page
      |
      v
foo(data)

However, this is not automatically exploitable in every modern environment.

Actual exploitability depends on factors such as:

  • Authentication mechanism

  • Cookie behavior

  • SameSite cookie settings

  • CSRF protections

  • Whether authentication credentials are automatically sent

  • Whether the endpoint supports JSONP

  • Whether the response is actually executable

  • Browser behavior

Therefore:

Don't conclude that "JSONP automatically allows an attacker to steal authenticated data."

The surrounding authentication and browser security controls matter.


13. Callback Injection

Another major JSONP security issue is unsafe handling of the callback parameter.

Suppose the server receives:

?callback=foo

and generates:

foo({...});

If the application simply concatenates the callback value into the response without validation, the callback parameter becomes a potential JavaScript injection point.

Conceptually:

callback=foo;ATTACKER_CODE//

could potentially result in something resembling:

foo({...});ATTACKER_CODE//({...});

The exact exploitability depends on:

  • Server-side parsing

  • Output encoding

  • Framework behavior

  • JavaScript syntax

  • Response construction

  • Validation

Therefore, callback parameters should be strictly validated.

A safer implementation should restrict the callback to an appropriate JavaScript identifier format rather than accepting arbitrary JavaScript syntax.

For example, conceptually:

Allowed:

foo
foo123
handleUser
myCallback

Rejected:

arbitrary JavaScript
quotes
semicolons
comments
expressions
function calls

Better still, applications should avoid exposing unnecessary user-controlled callback functionality.


14. JSONP and Authentication

This is where JSONP becomes particularly interesting from an AppSec perspective.

Consider:

https://victim.example/api/profile?callback=foo

If the endpoint returns user-specific information based on browser credentials, you need to ask:

What credentials does the browser send?

For example:

Cookies
Authorization headers
Client certificates
Other authentication mechanisms

For traditional cookie-based authentication, modern browser cookie policies such as:

SameSite
Secure
HttpOnly

can significantly affect whether a cross-site request is authenticated.

This means a JSONP vulnerability should always be analyzed in the context of the actual authentication mechanism, rather than assuming credentials will automatically accompany the request.


15. JSONP vs CORS

This is one of the most important comparisons.

JSONP

Browser
   |
   | <script>
   v
API
   |
   | JavaScript
   | callback({...})
   v
Browser
   |
   | Executes response
   v
Application

The API effectively says:

"Here is executable JavaScript."


CORS

Browser
   |
   | fetch()
   v
API
   |
   | Access-Control-Allow-Origin
   v
Browser
   |
   | Exposes response to JS
   v
fetch().then(...)

The API effectively says:

"This origin is allowed to read my response."

That is a much cleaner security model.


16. Why CORS Replaced JSONP

CORS allows APIs to explicitly control which origins can read their responses.

For example:

Access-Control-Allow-Origin: https://www.example.com

The browser can then allow JavaScript from that origin to access the response.

CORS also works with normal HTTP methods and APIs:

GET
POST
PUT
PATCH
DELETE

JSONP is limited primarily to the <script>/GET model.

Modern APIs therefore generally use:

fetch()
+
CORS

rather than:

<script>
+
JSONP

17. A Useful Mental Model

Don't think of JSONP as:

"JSON that bypasses CORS."

Think of it as:

A server-generated JavaScript program containing JSON data, loaded through a cross-origin <script> element.

That mental model makes the behavior much easier to understand.

JSON API

{
   "name": "Amar"
}

        ↓

        DATA


JSONP API

foo({
   "name": "Amar"
});

        ↓

   JAVASCRIPT
       +
      DATA

The difference is fundamental.


18. Security Review Checklist for JSONP

If you discover a JSONP endpoint during a penetration test, examine:

1. Is JSONP actually enabled?

Look for:

?callback=
?jsonp=
?cb=
?callbackFunction=

2. Is the endpoint sensitive?

Determine whether it returns:

User information
Account information
PII
Tokens
Internal data
Application configuration
Business information

3. Does authentication apply?

Determine:

Is the endpoint authenticated?
What authentication mechanism is used?
Are credentials automatically sent cross-site?
Are cookies SameSite protected?

4. Is the callback attacker-controlled?

Test whether:

callback=attackerControlledValue

is reflected into executable JavaScript.


5. Is callback validation implemented?

Check whether the server restricts callback names to a safe syntax.


6. What is the response Content-Type?

Ideally JSONP responses are explicitly treated as JavaScript:

Content-Type: application/javascript

Don't rely on MIME sniffing behavior.


7. Is JSONP actually necessary?

For modern applications, ask whether the functionality can be replaced with:

fetch()
+
CORS

19. The Most Important Takeaway

The entire JSONP mechanism can be summarized as:

                 JSONP

Client defines:
function foo(data) { ... }

             ↓

Client requests:

<script src=
"https://api.example.com/data?callback=foo">

             ↓

Server receives:

callback=foo

             ↓

Server generates:

foo({
   "name": "Amar"
});

             ↓

Browser treats response
as JavaScript

             ↓

Browser executes:

foo(data)

             ↓

Client JavaScript
receives the data

The critical security concept is:

JSONP works because <script> resources can be loaded cross-origin and their contents are executed as JavaScript. The server wraps JSON data inside a callback so the response becomes executable JavaScript.

That is also why JSONP has security risks that a normal JSON API does not have.


Final Mental Model

If you remember only this:

SOP
 │
 ├── Cross-origin fetch/XHR
 │       └── Response reading controlled by CORS
 │
 └── Cross-origin <script>
         └── Script can be loaded/executed
                    │
                    ▼
                  JSONP
                    │
                    ▼
          callback(JSON data)
                    │
                    ▼
             JavaScript executes

JSONP is therefore a legacy cross-origin data-sharing technique that turns data into executable JavaScript.

For application security, the two areas worth investigating most closely are JSONP-based sensitive-data exposure and callback-parameter injection.