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

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:

```text
https://www.example.com
```

and the API is hosted at:

```text
https://api.example.com
```

These are different origins because the hostname is different.

If JavaScript on `www.example.com` performs:

```javascript
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:

```text
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:

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

Your application might be:

```text
https://www.example.com
```

while the JavaScript is hosted at:

```text
https://cdn.example.com
```

The browser downloads the script and executes it.

So the browser has a mechanism that effectively allows:

```text
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:

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

Now imagine requesting it using:

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

The browser expects the response to be JavaScript.

But the server returned JSON:

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

That's not the JSONP pattern we want.

So JSONP changes the response.

Instead of returning:

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

the server returns:

```javascript
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:

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

The client then loads:

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

The server sees:

```text
callback=handleUser
```

and generates:

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

The browser receives the response and executes it.

The flow is:

```text
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

```html
<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

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

### Server Response

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

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

### Browser

The browser executes:

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

Therefore:

```javascript
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:

```html
<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:

```text
"Cross-origin JSON reading"
```

It is:

```text
"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:

```text
                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:

```text
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:

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

the request is essentially a GET request.

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

```text
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:

```json
{
  "name": "Amar"
}
```

the response is data.

With JSONP:

```javascript
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:

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

which returns:

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

An attacker might attempt to embed:

```html
<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:

```text
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:

```text
?callback=foo
```

and generates:

```javascript
foo({...});
```

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

Conceptually:

```text
callback=foo;ATTACKER_CODE//
```

could potentially result in something resembling:

```javascript
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:

```text
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:

```text
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:

```text
Cookies
Authorization headers
Client certificates
Other authentication mechanisms
```

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

```http
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

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

The API effectively says:

> "Here is executable JavaScript."

* * *

## CORS

```text
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:

```http
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:

```text
GET
POST
PUT
PATCH
DELETE
```

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

Modern APIs therefore generally use:

```text
fetch()
+
CORS
```

rather than:

```text
<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.

```text
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:

```text
?callback=
?jsonp=
?cb=
?callbackFunction=
```

* * *

### 2\. Is the endpoint sensitive?

Determine whether it returns:

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

* * *

### 3\. Does authentication apply?

Determine:

```text
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:

```text
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:

```http
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:

```text
fetch()
+
CORS
```

* * *

# 19\. The Most Important Takeaway

The entire JSONP mechanism can be summarized as:

```text
                 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:

```text
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**.
