JavaScript Hard technical 1 views 2 min read

How do you prevent prototype pollution attacks in JavaScript?

Peer-reviewed by HireXTech Technical Panel • Updated for 2025/2026 hiring • Editorial standards
Practise this track
Interviewer Expectations for this Question
01
Core Competency

Assesses fundamental understanding of JavaScript conventions, runtime behavior, and memory/performance considerations.

02
Evaluation Criteria

Hiring managers look for precision, avoidance of ambiguous jargon, and ability to explain trade-offs under real production conditions.

Comprehensive Model Answer Verified Solution

Prototype pollution is a security vulnerability where attackers inject properties into Object.prototype, affecting all objects in the application.

Vulnerable code:

     function merge(target, source) {
       for (let key in source) {
         target[key] = source[key];
       }
       return target;
     }

     // Attack payload
     const malicious = JSON.parse('{"__proto__": {"polluted": "yes"}}');
     merge({}, malicious);

     console.log({}.polluted); // "yes" - all objects are polluted!
     

Prevention 1: Use Object.create(null):

     // Create objects without prototype
     const safeObj = Object.create(null);
     safeObj.__proto__ = { polluted: 'yes' };
     console.log(safeObj.polluted); // undefined

     // For configuration objects
     const config = Object.create(null);
     config.apiUrl = 'https://api.example.com';
     

Prevention 2: Check for dangerous keys:

     function safeMerge(target, source) {
       const dangerousKeys = ['__proto__', 'constructor', 'prototype'];

       for (let key in source) {
         if (dangerousKeys.includes(key)) {
           continue; // Skip dangerous keys
         }
         if (source.hasOwnProperty(key)) {
           target[key] = source[key];
         }
       }
       return target;
     }
     

Prevention 3: Use Map instead of objects:

     const safeMap = new Map();
     safeMap.set('__proto__', 'value');
     // No pollution risk
     

Prevention 4: Freeze Object.prototype:

     Object.freeze(Object.prototype);
     Object.freeze(Object);

     // Now pollution attempts will fail
     Object.prototype.polluted = 'no';
     console.log({}.polluted); // undefined
     

Prevention 5: Validate object paths:

     function setDeepProperty(obj, path, value) {
       const parts = path.split('.');
       const dangerous = ['__proto__', 'constructor', 'prototype'];

       // Validate each part of the path
       if (parts.some(part => dangerous.includes(part))) {
         throw new Error('Invalid property path');
       }

       let current = obj;
       for (let i = 0; i < parts.length - 1; i++) {
         if (!(parts[i] in current)) {
           current[parts[i]] = {};
         }
         current = current[parts[i]];
       }

       current[parts[parts.length - 1]] = value;
     }
     

Prevention 6: Use libraries with protection:

     // Use lodash's merge with customizer
     const _ = require('lodash');

     function safeMergeCustomizer(objValue, srcValue, key) {
       const dangerous = ['__proto__', 'constructor', 'prototype'];
       if (dangerous.includes(key)) {
         return objValue; // Keep original value
       }
     }

     const result = _.mergeWith({}, source, safeMergeCustomizer);
     

Prevention 7: Schema validation:

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

     const schema = {
       type: 'object',
       properties: {
         name: { type: 'string' },
         age: { type: 'number' }
       },
       additionalProperties: false // Reject unknown properties
     };

     const validate = ajv.compile(schema);

     function safeProcess(data) {
       if (!validate(data)) {
         throw new Error('Invalid data');
       }
       return data;
     }
     

Prevention 8: JSON.parse with reviver:

     function safeJSONParse(text) {
       return JSON.parse(text, (key, value) => {
         const dangerous = ['__proto__', 'constructor', 'prototype'];
         if (dangerous.includes(key)) {
           return undefined; // Filter out dangerous keys
         }
         return value;
       });
     }

     const safe = safeJSONParse('{"__proto__": {"polluted": "yes"}}');
     console.log({}.polluted); // undefined
     

Candidate Response Strategy & Interview Tips

  1. Start with a concise one-sentence summary: Deliver a direct, confident answer first before expanding into nuances.
  2. Demonstrate real-world trade-offs: Discuss where this approach excels and when you would avoid it in production systems.
  3. Discuss complexity & edge cases: Proactively explain time/space complexity or boundary conditions (null values, scale limits).
  4. Prepare for interviewer follow-ups: Technical hiring panels frequently probe deeper into concurrency, backward compatibility, or alternative libraries.
Spotted an error or have an alternative solution?