Lab 05: Security in Node.js
Overview
Step 1: Prototype Pollution
// VULNERABLE: Recursive merge without sanitization
function deepMerge(target, source) {
for (const key of Object.keys(source)) {
if (source[key] && typeof source[key] === 'object') {
target[key] = target[key] ?? {};
deepMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
// Attack: pollute Object.prototype
const attack = JSON.parse('{"__proto__": {"isAdmin": true}}');
deepMerge({}, attack);
console.log({}.isAdmin); // true! — All objects now have isAdmin
// SECURE: Check for dangerous keys
function safeMerge(target, source, depth = 0) {
if (depth > 10) throw new Error('Max depth exceeded'); // Prevent deep recursion
for (const key of Object.keys(source)) {
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
continue; // Skip dangerous keys
}
if (source[key] && typeof source[key] === 'object') {
target[key] = target[key] ?? Object.create(null);
safeMerge(target[key], source[key], depth + 1);
} else {
target[key] = source[key];
}
}
return target;
}
// Use Object.create(null) for safe lookup tables
const safe = Object.create(null);
safe['__proto__'] = 'not a prototype!';
console.log(safe['__proto__']); // 'not a prototype!' (just a key)
// Reset pollution from demo (don't do this in production!)
delete Object.prototype.isAdmin;Step 2: ReDoS (Regular Expression Denial of Service)
Step 3: Path Traversal
Step 4: Deserialization & Injection
Step 5: Security Headers
Step 6: Input Validation
Step 7: Secure Coding Demo
Summary
Vulnerability
Attack
Defense
Last updated
