JSON Security: Preventing Injection Attacks
Protect your applications from JSON-based vulnerabilities and attacks
Last updated: January 21, 2026 • 10 min read
Common JSON Security Vulnerabilities
1. JSON Injection Attacks
JSON injection occurs when untrusted data is inserted into JSON without proper validation or escaping.
Vulnerable Code Example:
// ❌ DANGEROUS - Never do this!
const userInput = req.query.name; // User controls this
const json = `{"name": "${userInput}", "role": "user"}`;
const data = JSON.parse(json);Attack Example:
// Malicious input:
name = 'John", "role": "admin", "x": "'
// Results in:
{"name": "John", "role": "admin", "x": "", "role": "user"}
// User just became an admin!✅ Secure Solution:
// Use proper JSON serialization
const userInput = req.query.name;
const data = {
name: userInput, // JSON.stringify handles escaping
role: "user"
};
const json = JSON.stringify(data);2. Cross-Site Scripting (XSS) via JSON
When JSON data is rendered in HTML without sanitization, it can lead to XSS attacks.
Vulnerable Code:
// ❌ DANGEROUS
<script>
const userData = ${JSON.stringify(userInput)};
document.getElementById('name').innerHTML = userData.name;
</script>Attack Example:
// Malicious input: name: "<img src=x onerror='alert(document.cookie)'>" // Gets executed in the browser!
✅ Secure Solution:
// Use textContent instead of innerHTML
const userData = JSON.parse(safeJsonString);
document.getElementById('name').textContent = userData.name;
// Or sanitize HTML
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(userData.name);3. Mass Assignment Vulnerabilities
Accepting arbitrary JSON properties can allow attackers to modify fields they shouldn't have access to.
Vulnerable Code:
// ❌ DANGEROUS - Accepts any property
app.post('/api/users/:id', (req, res) => {
const updates = req.body; // Could contain anything!
await User.update(req.params.id, updates);
});Attack Example:
// Malicious request:
POST /api/users/123
{
"name": "John",
"role": "admin", // ❌ User shouldn't be able to set this
"isVerified": true, // ❌ Or this
"credits": 999999 // ❌ Or this
}✅ Secure Solution:
// Whitelist allowed fields
app.post('/api/users/:id', (req, res) => {
const allowedFields = ['name', 'email', 'bio'];
const updates = {};
for (const field of allowedFields) {
if (req.body[field] !== undefined) {
updates[field] = req.body[field];
}
}
await User.update(req.params.id, updates);
});4. Prototype Pollution
JSON parsing can be exploited to modify JavaScript object prototypes.
Vulnerable Code:
// ❌ DANGEROUS - Recursive merge without protection
function merge(target, source) {
for (const key in source) {
if (typeof source[key] === 'object') {
target[key] = merge(target[key] || {}, source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
const userInput = JSON.parse(req.body);
merge(config, userInput); // Vulnerable!Attack Example:
// Malicious JSON:
{
"__proto__": {
"isAdmin": true
}
}
// Now ALL objects have isAdmin: true!
console.log({}.isAdmin); // true - Oh no!✅ Secure Solution:
// Use Object.assign or spread operator with validation
function secureMerge(target, source) {
const safeSource = {};
for (const key in source) {
// Skip prototype pollution attempts
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
continue;
}
if (source.hasOwnProperty(key)) {
safeSource[key] = source[key];
}
}
return { ...target, ...safeSource };
}5. Denial of Service (DoS) Attacks
Large Payload Attack:
// Attacker sends extremely large JSON // 100MB+ of data to crash server
✅ Protection:
// Express.js - Limit payload size
app.use(express.json({
limit: '1mb' // Maximum 1MB
}));
// Or custom middleware
app.use((req, res, next) => {
const contentLength = req.headers['content-length'];
if (contentLength > 1048576) { // 1MB
return res.status(413).json({
error: 'Payload too large'
});
}
next();
});JSON Security Best Practices
1. Always Validate Input
// Use JSON Schema for validation
const Ajv = require('ajv');
const ajv = new Ajv();
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'],
additionalProperties: false // Reject unknown fields
};
const validate = ajv.compile(schema);
if (!validate(data)) {
throw new Error('Invalid data: ' + JSON.stringify(validate.errors));
}2. Use Content-Type Headers Correctly
// Server-side
res.setHeader('Content-Type', 'application/json; charset=utf-8');
// Client-side validation
if (response.headers.get('Content-Type') !== 'application/json') {
throw new Error('Expected JSON response');
}3. Implement Rate Limiting
const rateLimit = require('express-rate-limit');
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per window
message: 'Too many requests, please try again later'
});
app.use('/api/', apiLimiter);4. Sanitize Before Output
// For HTML context import DOMPurify from 'dompurify'; const clean = DOMPurify.sanitize(jsonData.userInput); // For JavaScript context const escaped = JSON.stringify(jsonData) .replace(/</g, '\u003c') .replace(/>/g, '\u003e');
5. Use HTTPS Always
// Redirect HTTP to HTTPS
app.use((req, res, next) => {
if (!req.secure && process.env.NODE_ENV === 'production') {
return res.redirect('https://' + req.headers.host + req.url);
}
next();
});6. Implement CORS Properly
const cors = require('cors');
// Don't use wildcard in production!
app.use(cors({
origin: ['https://yourdomain.com'], // Whitelist specific domains
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
}));7. Log Security Events
// Log suspicious activity
app.use((req, res, next) => {
try {
const data = JSON.parse(req.body);
// Check for suspicious patterns
if (JSON.stringify(data).includes('__proto__')) {
logger.warn('Prototype pollution attempt detected', {
ip: req.ip,
body: req.body
});
return res.status(400).json({ error: 'Invalid request' });
}
next();
} catch (error) {
logger.error('JSON parsing error', { error: error.message });
res.status(400).json({ error: 'Invalid JSON' });
}
});Security Checklist
✅ JSON Security Checklist
- ✅ Never construct JSON strings manually with user input
- ✅ Always use JSON.stringify() and JSON.parse()
- ✅ Validate all JSON input with schemas
- ✅ Whitelist allowed fields (no mass assignment)
- ✅ Sanitize output before rendering in HTML
- ✅ Limit payload sizes to prevent DoS
- ✅ Check for prototype pollution attempts
- ✅ Use HTTPS for all JSON API calls
- ✅ Implement proper CORS policies
- ✅ Rate limit API endpoints
- ✅ Log security-related events
- ✅ Keep dependencies updated
Tools for Secure JSON Handling
Conclusion
JSON security is critical for protecting your applications and users. By following these best practices and staying vigilant against common vulnerabilities, you can significantly reduce your attack surface. Remember: never trust user input, always validate, and sanitize before output.
Secure Your JSON Data
Start by validating your JSON to ensure it's well-formed and safe!
Validate JSON Free →