Security

JSON Security: Best Practices and Common Vulnerabilities

December 20, 2024 11 min read

Common JSON Security Vulnerabilities

1. Never Use eval() to Parse JSON

❌ DANGEROUS - Code Injection Risk
// NEVER do this!
const obj = eval('(' + jsonString + ')');

// Attacker can inject:
const malicious = '({}).constructor.constructor("alert(1)")()';
✅ SAFE - Always Use JSON.parse()
const obj = JSON.parse(jsonString);

2. Validate Input Schema

Never trust client data. Always validate structure and types:

// Bad - No validation
const user = JSON.parse(request.body);
database.save(user);

// Good - Validate first
const schema = {
  type: "object",
  properties: {
    name: { type: "string", maxLength: 100 },
    age: { type: "number", minimum: 0, maximum: 150 },
    email: { type: "string", format: "email" }
  },
  required: ["name", "email"]
};

const user = JSON.parse(request.body);
if (!validateSchema(user, schema)) {
  throw new Error("Invalid input");
}

3. Prevent Prototype Pollution

⚠️ Vulnerability
// Attacker payload
{
  "__proto__": {
    "isAdmin": true
  }
}

// This can pollute Object.prototype!
const user = JSON.parse(attackerJson);
Object.assign({}, user);  // Dangerous!
✅ Protection
// Use Object.create(null) to avoid prototype
const user = Object.assign(Object.create(null), JSON.parse(jsonString));

// Or use a safe merge library
const user = safeMerge({}, JSON.parse(jsonString));

4. Set Payload Size Limits

// Express.js example
app.use(express.json({
  limit: '1mb',
  strict: true
}));

// Manual check
if (jsonString.length > 1024 * 1024) {
  throw new Error('Payload too large');
}

5. Sanitize Output

When embedding JSON in HTML, always escape:

// Bad - XSS vulnerability
<script>
  const data = <%= JSON.stringify(userData) %>;
</script>

// Good - Escaped
<script>
  const data = <%= JSON.stringify(userData).replace(/</g, '\\u003c') %>;
</script>

6. Avoid Exposing Sensitive Data

// Bad - Exposing password
{
  "user": {
    "name": "John",
    "email": "john@example.com",
    "password": "hashedpassword",  // Never send this!
    "ssn": "123-45-6789"           // Never send this!
  }
}

// Good - Only send what's needed
{
  "user": {
    "name": "John",
    "email": "john@example.com"
  }
}

7. Implement Rate Limiting

// Prevent DoS attacks
const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
});

app.use('/api/', limiter);

8. Use HTTPS for Transmission

Always transmit JSON over HTTPS to prevent:

  • Man-in-the-middle attacks
  • Data interception
  • Session hijacking

9. Validate Content-Type Header

// Server-side validation
if (request.headers['content-type'] !== 'application/json') {
  return response.status(400).send('Invalid Content-Type');
}

10. Use JSON Schema Validation

const Ajv = require('ajv');
const ajv = new Ajv();

const schema = {
  type: "object",
  properties: {
    username: { type: "string", pattern: "^[a-zA-Z0-9_]+$" },
    email: { type: "string", format: "email" }
  },
  required: ["username", "email"],
  additionalProperties: false  // Reject unknown properties
};

const validate = ajv.compile(schema);
const valid = validate(data);

if (!valid) {
  console.log(validate.errors);
}

Security Checklist

✅ JSON Security Checklist
  • ☐ Never use eval() for parsing
  • ☐ Always validate input schema
  • ☐ Protect against prototype pollution
  • ☐ Set payload size limits
  • ☐ Sanitize output when embedding in HTML
  • ☐ Never expose sensitive data
  • ☐ Implement rate limiting
  • ☐ Use HTTPS for transmission
  • ☐ Validate Content-Type headers
  • ☐ Use JSON Schema validation
  • ☐ Log and monitor suspicious activity
  • ☐ Keep libraries up to date

Recommended Libraries

  • Validation: Ajv, joi, yup
  • Sanitization: DOMPurify, sanitize-html
  • Rate Limiting: express-rate-limit
  • Security Headers: helmet.js

Conclusion

JSON security is crucial for protecting your applications and users. Always validate input, sanitize output, and follow security best practices. Never trust client data and always assume input is malicious until proven otherwise.

Test your JSON: Use our JSON Validator to ensure your JSON is well-formed before processing.

Back to Blog