Why XSS Is Not (Usually) Possible in JSON Responses: A Deep Dive

When performing web application penetration tests, you'll likely encounter APIs returning JSON data. A common question is:

"Can XSS occur in JSON responses?"

The short answer: not by default. But the long answer is much more interesting — and important to understand for both secure coding and effective testing. In this post, we'll break down why JSON isn't inherently vulnerable to XSS, how it can become vulnerable, and what security best practices you should follow.

None

How Browsers Handle JSON

Unlike HTML, raw JSON served with Content-Type: application/json is treated purely as data. Browsers do not execute the contents—even if it includes things like <script>alert(1)</script>.

For example:{"user":"<script>alert(1)</script>"}

This won't trigger an alert because browsers don't parse JSON as HTML or JavaScript.

Modern browsers, especially with the X-Content-Type-Options: nosniff header set, won't attempt to interpret JSON as anything executable. They respect the MIME type and leave it inert.

Why JSON Can't Execute Scripts (By Itself)

JSON is a structured data format — like XML or CSV. It contains values (strings, arrays, numbers, booleans) but not logic or script context.

Even when JSON is parsed via JavaScript (JSON.parse()), it converts the data to an object without evaluating any executable code inside the strings.

Unless a developer does something unsafe like eval(jsonString)—which is highly discouraged—the data will remain passive.

How JSON Can Still Lead to XSS

While JSON isn't inherently dangerous, it can become dangerous in the following cases:

1. Serving JSON as HTML

If your API mistakenly sets Content-Type: text/html instead of application/json, a browser might render the response as a webpage. If that JSON includes script tags, they may execute.

2. Injecting JSON into HTML Without Escaping

Example:

<script type="application/json">
  { "name": "</script><script>alert(1)</script>" }
</script>

This breaks the </script> tag, closes it early, and inserts a malicious script. Always escape user data when injecting it into HTML.

3. Unsafe DOM Insertion

Consider this vulnerable snippet:

fetch('/api/user')
  .then(res => res.json())
  .then(data => {
    userDiv.innerHTML = data.username;  // Dangerous
  });

If username contains something like <img src=x onerror=alert(1)>, it will execute.

Instead, use safe sinks:

userDiv.textContent = data.username;  // Safe

4. JSONP and eval()

If a page uses eval() to parse JSON (instead of JSON.parse()), malicious payloads can execute. Also, JSONP (JSON with Padding) returns actual JavaScript, not pure JSON. It's outdated and inherently risky.

5. Stored XSS via JSON

If user input with XSS payloads is stored as JSON and later embedded into a web page unsafely, the malicious content can eventually get executed — even if it wasn't dangerous when first submitted.

Secure Usage: Examples

Safe:

fetch('/api/comment')
  .then(res => res.json())
  .then(data => {
    commentDiv.textContent = data.text;  // Safe
  });

Unsafe:

fetch('/api/comment')
  .then(res => res.json())
  .then(data => {
    commentDiv.innerHTML = '<b>' + data.text + '</b>';  // Vulnerable to XSS
  });

Embedded JSON in HTML (safe):

<script>
  var data = JSON.parse("{{ json_data|escapejs }}");
</script>

Escape characters like < to \u003c.

Best Practices for Developers & Pentesters

Developers:

  • Always return JSON with Content-Type: application/json
  • Set X-Content-Type-Options: nosniff
  • Escape <, >, &, ", and ' if injecting into HTML
  • Avoid innerHTML, eval, and document.write()
  • Use .textContent or .setAttribute() instead

Pentesters:

  • Check MIME types and response headers
  • Test what happens if JSON data reaches innerHTML or other sinks
  • Look for stored payloads that may become dangerous later
  • Flag endpoints using JSONP
  • Validate if embedded JSON in pages is properly escaped

Conclusion

XSS in JSON isn't about the format — it's about how the data is used. JSON won't execute itself, but it can carry dangerous payloads that do execute if mishandled by client or server code.

JSON is safe. Mishandling JSON is not.

As a penetration tester, knowing these nuances helps you avoid false positives and identify real vulnerabilities. As a developer, applying these principles can keep your apps secure without compromising functionality.

Have thoughts, or war stories about JSON XSS? Drop a comment below.

#infosec #xss #pentesting #json #websecurity #owasp