JSONP and Content Security Policy

Sunday, June 8, 2025
10 min read
Theotime QuereTheotime Quere
JSONP (JavaScript Object Notation with Padding) is an older technique that was used to fetch data from different domains before modern solutions like CORS existed. While it served its purpose, it can create security vulnerabilities, especially when combined with Content Security Policy.

The same-origin policy and script tags

Key Concept

The Same-Origin Policy blocks regular HTTP requests to different origins, but it has a special exception for <script> tags. This exception is what makes JSONP possible.

How cross-origin requests normally work

Example of a blocked cross-origin request

// This will be blocked by the Same-Origin Policy
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data));

// Error: Access to fetch at 'https://api.example.com/data' from origin 
// 'https://yourdomain.com' has been blocked by CORS policy

The script tag exception

Example of how script tags bypass the Same-Origin Policy

// This works! The browser allows loading scripts from any origin
<script src="https://api.example.com/script.js"></script>

// This is why JSONP works - it uses this exception
<script src="https://api.example.com/data?callback=processData"></script>

Why This Works

Browsers allow <script> tags to load resources from any origin because historically, websites needed to load common JavaScript libraries from CDNs. This exception is what JSONP exploits.

The JSONP trick

  • Regular Cross-Origin Request

    What happens with normal requests

    • Browser makes HTTP request to different origin
    • Same-Origin Policy blocks the response
    • JavaScript can't access the data
    • CORS headers are required to allow access
  • JSONP Request

    How JSONP bypasses this restriction

    • Browser loads a <script> tag from different origin
    • Same-Origin Policy allows this because it's a script
    • Server returns JavaScript code (not just data)
    • Browser executes the code, giving access to the data

Visual comparison of regular request vs JSONP

// Regular request (blocked by Same-Origin Policy)
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data));

// JSONP request (allowed because it's a script)
<script src="https://api.example.com/data?callback=processData"></script>
// Server responds with: processData({"name": "John"});
// Browser executes this as JavaScript

Security Implications

This exception in the Same-Origin Policy is why JSONP can be dangerous. The browser treats the response as executable code, which is why proper validation of the callback parameter is crucial.

Why JSONP was used instead of CORS

Historical Context

JSONP was developed in the early days of web development when CORS wasn't widely supported. It provided a way to make cross-origin requests when browsers didn't support modern security features.

Browser support and limitations

  • Browser Support

    Why JSONP was necessary

    • CORS was introduced in 2006 but took years to be widely supported
    • Older browsers may not support CORS, but JSONP worked in all browsers, even very old ones
    • No server-side changes were required for JSONP
  • Technical Limitations

    What JSONP could and couldn't do

    • Only supported GET requests (no POST, PUT, DELETE)
    • No proper error handling mechanism
    • No support for custom headers
    • Limited to loading script resources

Example of browser support differences

// JSONP works in all browsers
<script src="https://api.example.com/data?callback=processData"></script>

// CORS requires browser support
fetch('https://api.example.com/data', {
  method: 'POST',
  headers: {
      'Content-Type': 'application/json'
  },
  body: JSON.stringify({ data: 'value' })
});

Why developers chose JSONP

Developer Perspective

JSONP was popular because it was simple to implement and worked everywhere. It didn't require server configuration or browser support, making it an attractive solution for cross-origin requests.

Example of JSONP vs CORS implementation complexity

// JSONP Implementation
function getData() {
  const script = document.createElement('script');
  script.src = 'https://api.example.com/data?callback=processData';
  document.body.appendChild(script);
}

// Classic Fecth that requires CORS
async function getData() {
  try {
      const response = await fetch('https://api.example.com/data', {
      headers: {
          'Accept': 'application/json'
      }
      });
      const data = await response.json();
      return data;
  } catch (error) {
      console.error('Error:', error);
  }
}

Understanding JSONP in detail

The JSONP process

Complete JSONP request and response flow

// 1. Client creates a script tag
const script = document.createElement('script');
script.src = 'https://api.example.com/data?callback=processData';
document.body.appendChild(script);

// 2. Server receives request and wraps data in callback
// Server response:
processData({"name": "John", "age": 30});

// 3. Browser executes the response as JavaScript
function processData(data) {
  console.log(data); // {name: "John", age: 30}
}

Security Risk

The server trusts the callback parameter from the client. If not properly validated, this trust can be exploited to inject malicious code.

Content security policy and JSONP

How CSP works

Example of a basic CSP policy

// CSP policy
Content-Security-Policy: script-src 'self' https://trusted-cdn.com;
Content Security Policy is a security feature that helps protect websites from attacks like Cross-Site Scripting (XSS). It lets website owners control which sources can load content on their site. Think of CSP as a security guard that only allows trusted resources to run on your website. It's like having a bouncer who only lets in people from an approved list.

For example, if you set a CSP policy to only allow scripts from your own domain and a trusted CDN, the browser will block scripts from other sources. This helps stop malicious code from running on your website.

To learn more about CSP and how to use it, check out our detailed guide on Content Security Policy.

CSP's Role

CSP acts as a security guard, specifying which domains are allowed to provide scripts for your website. It's designed to prevent attacks like Cross-Site Scripting (XSS).

The JSONP-CSP bypass

CSP Bypass Risk

If a whitelisted domain hosts a vulnerable JSONP endpoint, attackers can bypass CSP by injecting malicious code through the callback parameter.

CSP policy

Content-Security-Policy: script-src 'self' https://api.trustedpartner.com;

Example of a CSP bypass using JSONP

// Attacker's payload
<script src="https://api.trustedpartner.com/data?callback=alert(document.cookie)//"></script>

// Server response (executed due to CSP trust)
alert(document.cookie)//({"data": "sensitive"});

Detect JSONP endpoints

Instantly analyze your website's Content Security Policy and find the JSONP endpoints that could be exploited.

Scan Your Website

Enter your website URL to analyze its Content Security Policy configuration.

Get started now by providing your website URL and launch the scan!

Your website is not yet online?
Try our CSP Evaluator

Security risks beyond CSP bypass

  • Cross-Site Scripting (XSS)

    JSONP vulnerabilities can lead to XSS attacks

    • Malicious code injection through callback parameter
    • Execution of arbitrary JavaScript
    • Access to sensitive browser data
  • Data Theft (JSONP Hijacking)

    Attackers can steal user data

    • Create malicious website calling JSONP endpoint
    • Exploit automatic cookie sending
    • Capture sensitive user information

Modern solutions

1. Switch to CORS

Example of a secure CORS implementation

// Server response headers
Access-Control-Allow-Origin: https://yourdomain.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type

// Client-side code
fetch('https://api.example.com/data', {
  method: 'POST',
  headers: {
      'Content-Type': 'application/json'
  },
  body: JSON.stringify({ data: 'value' })
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));

2. Implement Strict CSP

Example of a strict CSP using nonces

// Server response header
Content-Security-Policy: script-src 'nonce-random123' 'strict-dynamic';

// HTML
<script nonce="random123">
// Your code here
</script>

Example of a strict CSP using hashes

// Server response header
Content-Security-Policy: script-src 'sha256-hashvalue' 'strict-dynamic';

// HTML
<script>
// Your code here (content must match the hash)
</script>

Migration Tips

When migrating from JSONP to CORS, start with non-critical endpoints, test thoroughly, and ensure proper error handling is in place. Consider using feature detection to support older browsers during the transition.

Secure your website with proper CSP configuration

Join thousands of developers who trust CentralCSP to protect their websites.

Start with a 14 days free trial

Conclusion

While JSONP was once a useful technique, it's now considered outdated and potentially dangerous. Modern web applications should use CORS for cross-origin requests and implement strict CSP policies using nonces or hashes. This combination provides better security and follows current best practices for web development. Remember to plan your migration carefully, validate all inputs, and maintain strict security policies throughout your application.