JSON Security: Defense Against Injection and Key Collisions

#1JSON security: parser differences that can break assumptions
What we tested: We processed API response payloads ranging from 1 KB to 50 MB through each JSON tool on this site. Formatting, validation, and conversion times were measured in Chrome 128 with DevTools performance panel. All processing runs locally in the browser, no server round-trips.
JSON feels simple until different parsers disagree about the same payload.
That matters when a document crosses system boundaries, because duplicate keys, nesting depth, and oversized values can be interpreted differently by each side.
#21. JSON Key Collisions: The Parser Asymmetry Attack
A "Key Collision" (also called "Duplicate Key Attack") occurs when a JSON object contains the same key twice:
{
"user_id": 123,
"user_id": 456
}This is technically invalid JSON per RFC 8259, which states "names within an object should be unique." However, the RFC uses "should" (RECOMMENDED) rather than "must" (REQUIRED), leaving the behavior of duplicate keys up to the parser.
#3How Different Languages Handle Duplicates
| Language/Library | Behavior |
|---|---|
Python json.loads() | Last value wins (456) |
JavaScript JSON.parse() | Last value wins (456) |
| Java Jackson | First value wins (123) |
| Java Gson | Last value wins (456) |
Go encoding/json | Last value wins (456) |
PHP json_decode() | Last value wins (456) |
Ruby JSON.parse | Last value wins (456) |
| Some XML/SOAP parsers | Implementation-defined |
#3The Attack Scenario
This inconsistency is the basis for real privilege escalation attacks in multi-layer architectures:
{
"user_id": 1337,
"role": "admin",
"user_id": 42,
"role": "user"
}Layer 1 (Security Gateway): Java Jackson parser reads the first values → user_id: 1337, role: admin. Security check passes.
Layer 2 (Application Server): Python json.loads() reads the last values → user_id: 42, role: user. Attacker has elevated privileges in the gateway but the application sees different data.
Real CVE examples: This pattern has been observed in:
- JWT parsers where a duplicate
algclaim causes one parser to validate with RS256 and another to skip validation - GraphQL servers where the same field name appears twice in a query
- Cookie parsing libraries where duplicate
sessionparameters cause session fixation
#3Defense Against Key Collisions
1. Reject duplicates at the parser level: Configure your parser to throw an error on duplicate keys.
# Python: reject duplicate keys
import json
def reject_duplicates(pairs):
result = {}
for key, value in pairs:
if key in result:
raise ValueError(f"Duplicate key: {key}")
result[key] = value
return result
data = json.loads(json_string, object_pairs_hook=reject_duplicates)// JavaScript: no built-in protection, use a schema validator
// JSON.parse() does NOT reject duplicates
// Use a schema validator that enforces unique keys2. Normalize before processing: Pass all incoming JSON through a single canonical parser at your system boundary, then re-serialize before passing to downstream services. Now every service sees the same representation instead of parsing the raw bytes its own way.
3. Schema validation: A strict JSON Schema with "additionalProperties": false and explicit property definitions rejects payloads with unexpected structure, though it doesn't specifically reject duplicate keys unless your validator is configured to do so.
Use the JSON Validator to check for structural anomalies including duplicate keys before your data reaches application logic.
#22. Resource Exhaustion: The "Billion Laughs" JSON Variant
XML's "Billion Laughs" attack (CVE-2003-1564) uses entity references to expand a small XML file into gigabytes of data. JSON doesn't support entities, but it has its own resource exhaustion vectors.
#3Deeply Nested Objects
{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":...}}}}}}}}}}}When a parser attempts to deserialize a JSON object nested 10,000 levels deep, it typically uses recursion. At 10,000 levels, this triggers a stack overflow in most languages, a hard crash rather than a graceful error.
Language depth limits by default:
- Python: 1024 levels (configurable)
- JavaScript
JSON.parse(): Implementation-defined, typically 20,000+ - Java Jackson: 1000 levels by default
- Go
encoding/json: No built-in limit
Attackers can trigger stack overflows (crashing the process) or excessive memory allocation (OOM kill) with a single small HTTP request.
#3Extremely Large Numbers
{"value": 99999999999999999999999999999999999999999999999999999}Depending on the language and parser, this may:
- Parse as
Infinity(JavaScript) - Throw an overflow exception (Java)
- Parse as a BigInteger consuming significant memory (Python)
- Cause undefined behavior
#3Very Long Strings
{"key": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa..."}A JSON body with a single string value of 10MB can consume significantly more memory when parsed into an object in some languages due to internal string representation overhead.
#3Defense Against Resource Exhaustion
1. Set limits at the edge: Configure your web server or API gateway to reject payloads above a maximum size before they reach your application:
# Nginx: reject request bodies over 1MB
client_max_body_size 1m;# FastAPI: limit request body size
from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError
@app.middleware("http")
async def limit_upload_size(request: Request, call_next):
content_length = request.headers.get("content-length")
if content_length and int(content_length) > 1_000_000:
return Response("Request too large", status_code=413)
return await call_next(request)2. Configure parser depth limits:
# Python: custom recursion limit for JSON
import json
import sys
# Reduce max recursion depth for JSON parsing
old_limit = sys.getrecursionlimit()
sys.setrecursionlimit(100)
try:
data = json.loads(untrusted_json)
finally:
sys.setrecursionlimit(old_limit)3. Validate structure before deserializing: Use a streaming JSON parser to validate structure and extract metrics (depth, key count, string lengths) before allocating memory for the full deserialized object.
#23. Prototype Pollution: The JavaScript/Node.js Threat
If you're using Node.js, JSON parsing presents a unique threat: Prototype Pollution. This is one of the most dangerous JavaScript-specific vulnerabilities and has resulted in remote code execution in production applications.
#3How Prototype Pollution Works
In JavaScript, every object inherits from Object.prototype. If an attacker can modify Object.prototype, they can add or overwrite properties that are inherited by every object in the application, including security-critical checks.
// Attacker sends this JSON payload
{
"__proto__": {
"isAdmin": true
}
}When code merges this payload into an application object without sanitization:
// VULNERABLE: deep merge without protection
function merge(target, source) {
for (const key of Object.keys(source)) {
if (typeof source[key] === 'object') {
target[key] = merge(target[key] || {}, source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
const config = merge({}, JSON.parse(attackerPayload));
// Now: ({}).isAdmin === true, for every object in the application!After this operation, ({}).isAdmin evaluates to true for every object, including the security check if (user.isAdmin).
#3Real-World Impact of Prototype Pollution
- CVE-2019-10744: Prototype pollution in
lodashaffecting millions of npm packages - CVE-2020-8116: Prototype pollution in
dot-proppackage - CVE-2019-10746: Prototype pollution in
mixin-deep
Prototype pollution can lead to:
- Authentication bypass
- Remote code execution via gadget chains
- Denial of service through property deletion
- Security filter bypass
#3Defense Against Prototype Pollution
1. Use JSON.parse() safely: JSON.parse() itself is safe, the vulnerability arises when the parsed object is merged into other objects without sanitization.
2. Reject "magic" keys before merging:
const FORBIDDEN_KEYS = ['__proto__', 'constructor', 'prototype'];
function safeMerge(target, source) {
for (const key of Object.keys(source)) {
if (FORBIDDEN_KEYS.includes(key)) {
throw new Error(`Forbidden key: ${key}`);
}
// ... merge logic
}
}3. Use Object.create(null) for untrusted data:
// Parse into a null-prototype object to prevent pollution
const data = JSON.parse(untrustedInput);
const safeData = Object.assign(Object.create(null), data);
// safeData has no prototype - can't be used to pollute Object.prototype4. Schema validation before merge: Validate against a strict JSON Schema that explicitly defines allowed properties. Any __proto__ key in the payload will fail validation before the merge occurs.
Use the JSON Schema Validator to define your expected payload structure with "additionalProperties": false and "patternProperties" restrictions that prevent magic-key injection.
#24. JSON Injection: When JSON Becomes a Payload
JSON injection occurs when user-supplied data is concatenated into a JSON string without proper escaping, allowing an attacker to inject JSON structure.
#3The Attack
// VULNERABLE: string concatenation to build JSON
const username = `"; "admin": true, "extra": "`;
const payload = `{"user": "${username}", "role": "user"}`;
// Resulting JSON: {"user": ""; "admin": true, "extra": "", "role": "user"}
// After parsing: { user: '', admin: true, extra: '', role: 'user' }The attacker escapes the user string context and injects additional JSON properties.
#3Defense Against JSON Injection
Never build JSON by string concatenation. Always use a proper serializer:
// SAFE: use JSON.stringify() to build JSON
const payload = JSON.stringify({
user: username, // Properly escaped by JSON.stringify
role: "user"
});JSON.stringify() properly escapes all special characters in string values. The attacker's payload becomes {"user":"\\"; \\\"admin\\\": true...","role":"user"}, harmless escaped data.
#25. The Large Payload Validation Challenge
Validating large JSON files (megabytes of logs, export data, or configuration) is essential, but creates a privacy dilemma: sending multi-megabyte files containing internal paths, user data, or system information to a cloud validator exposes sensitive data to a third party.
The AllDevToolsHub JSON Validator and JSON Schema Validator solve this by performing all validation locally in your browser using Web Workers. Whether it's a 10KB config or a 10MB log file:
- Parsing and validation happen entirely in your browser's dedicated thread
- No data is transmitted to any server
- Large files don't freeze the UI thanks to Web Worker threading
- You can verify this with the browser's Network tab (zero data requests)
This is particularly critical for validating:
- Production
.envequivalents in JSON format - API responses containing customer PII during debugging
- Internal service configurations with sensitive endpoints
- Database export files with schema information
#26. JSON Security Checklist
Build these checks into your API and service development workflow:
| Check | When to Apply | Tool |
|---|---|---|
| Reject duplicate keys | All JSON parsing from untrusted sources | Custom parser hook or JSON Schema |
| Validate against schema | All API endpoints accepting JSON | JSON Schema Validator |
| Limit depth/size | Edge/gateway layer | Web server config + custom middleware |
| Sanitize before merge | Any code that merges parsed JSON | Allowlist approach, FORBIDDEN_KEYS filter |
Use JSON.stringify() | When building JSON from user data | Language-native serializer |
| Audit magic keys | Any dynamic object merge | __proto__, constructor, prototype |
| Local validation | Sensitive production data | AllDevToolsHub (browser-local) |
#2Summary: Close the JSON Security Gap
JSON's ubiquity makes it a rich attack surface that most developers don't think about deeply. The key insight: the danger isn't in the JSON format itself, it's in the implementation differences between parsers and in missing validation at trust boundaries.
A defense that actually holds needs four things:
- Validation at every trust boundary (JSON Schema + structural checks)
- Rejecting ambiguity (no duplicate keys, no magic keys)
- Defense in depth (edge limits + parser limits + application validation)
- Local-first tooling for sensitive data audit
Secure your JSON handling at the AllDevToolsHub JSON Security Hub.
#2Related Tools
- JSON Validator, Validate JSON syntax and detect structural anomalies locally
- JSON Schema Validator, Enforce strict schemas to prevent injection and pollution
- JSON Formatter, Format and inspect large JSON payloads in your browser
#2Related Articles
- Stop Trusting LLM JSON: Validate AI Outputs with Zod
- JWT Tokens Explained: Decode, Verify & Common Mistakes
- Stop Vibe Coding: The Engineering Case for Structural Validation
#2Frequently Asked Questions
Q: Is prototype pollution only a concern in Node.js?
A: Prototype pollution is a JavaScript-specific vulnerability due to JavaScript's prototype-based inheritance model. It doesn't apply to Python, Go, Java, or Rust backends. However, any JavaScript that runs on the server (Node.js) or in the browser with sensitive operations is vulnerable.
Q: Does using TypeScript protect against prototype pollution?
A: No. TypeScript adds static types at compile time but the compiled JavaScript still runs at runtime with the same vulnerability surface. The protection must come from runtime validation and sanitization, not type annotations.
Q: What's the best JSON Schema validator for Node.js?
A: ajv (Another JSON Validator) is the most widely used and performant JSON Schema validator for Node.js. It supports JSON Schema drafts 2019-09 and 2020-12 and is used internally by many major frameworks including Next.js and Fastify.
Q: How do I validate JSON in a streaming context (very large files)?
A: Use a streaming JSON parser that processes the document incrementally without loading the entire structure into memory. Libraries like streaming-json-parser (Node.js) or ijson (Python) allow you to validate structure and extract specific fields from gigabyte-sized JSON files without OOM risk.
Q: Can I trust a third-party JSON validation API with sensitive data?
A: No. Any JSON data you send to a third-party API could be logged, stored, or exposed. Use the AllDevToolsHub JSON Validator, which runs entirely in your browser, no server ever receives your data. This is essential for validating JSON containing credentials, customer data, or internal configuration.
Written by Rahul Jalavadiya, founder of AllDevToolsHub. All tools run locally in your browser.
#2Sources / Further reading
- IETF - RFC 8259: JSON
- OWASP - Injection Prevention Cheat Sheet
- CVE Details - JSON parser vulnerabilities
- PortSwigger - JSON web signature attacks
Quick Summary
>- JSON is the language of the web, but it's not inherently safe. Learn how to secure your JSON parsers against modern attack vectors.
Tools Mentioned in This Article
API Response Diff
Compare two API responses to find structural differences.
Mock API Generator
Generate boilerplate for API mocks (Axios, Fetch, Express).
AES Encrypt / Decrypt
Encrypt and decrypt text with AES-256-GCM and PBKDF2 key derivation, 100% client-side.
JSON ↔ CSV Converter (Pro)
Professional grade JSON/CSV converter with delimiter detection.
Tools, tactics, and toughened-up tips, once a week
New tools, deep-dives on developer workflows, and the occasional gem we found this week. No spam, no tracking. Unsubscribe anytime.
Found an error or have feedback?
We correct errors quickly and document changes in our changelog. Report issues at support@alldevtoolshub.com.