Mahamudul Hasan Rubel
HomeBlogCoursesAboutProjectsSkillsExperiencePhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • Blog
  • Courses
  • About
  • Projects
  • Skills
  • Experience
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

Subscribe to the newsletter

Get new articles and course lessons delivered to your inbox. No spam, unsubscribe anytime.

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
SecurityJune 26, 20264 min read

Node.js Security: Preventing Prototype Pollution in Data Merging

Node.js security depends on preventing prototype pollution. Learn how to harden your object merging logic and stop malicious object injection in production.

Node.jsSecurityPrototype PollutionSecure CodingJavaScriptWebBackend

During a recent audit of a legacy Node.js service, I found a recursive merge function that was essentially a backdoor waiting to happen. It was a classic utility function used to deep-merge user-provided configuration objects into our base settings, but it lacked any protection against property name injection. A simple JSON payload could overwrite the toString method on the Object.prototype, causing the entire application to crash the next time it tried to log an object.

If you’re working with Node.js, you’ve likely used a merge or extend utility at some point. These functions are dangerous when they process untrusted input without validation. If you don't secure these operations, you're leaving the door wide open for attackers to modify the behavior of every object in your application.

Understanding Prototype Pollution in Node.js

At its core, Node.js security is about controlling the boundary between untrusted input and your runtime environment. Prototype pollution occurs when an attacker injects properties into an object's prototype chain. Because JavaScript objects share prototypes, modifying the base Object.prototype affects every object in your memory space.

When an attacker sends a JSON payload containing __proto__, constructor, or prototype keys, they can inject malicious functions or configuration overrides. If your code recursively merges this payload, it might accidentally assign values to these sensitive keys.

We first tried to fix this by simply filtering out the __proto__ string. It broke within an hour because we didn't account for constructor.prototype or nested variations. That’s when we realized that blacklisting is a losing game.

Hardening Data Merging and Object Initialization

To stop prototype pollution, you need to sanitize your inputs before they touch your merging logic. You should never merge objects that haven't been validated against a strict schema.

Here is a defensive pattern I’ve started using in production. It ignores any key that could potentially lead to object injection:

JAVASCRIPT
function isSafeKey(key) {
  return key !== CE9178">'__proto__' && key !== CE9178">'constructor' && key !== CE9178">'prototype';
}

function secureMerge(target, source) {
  for (const key in source) {
    if (isSafeKey(key)) {
      if (typeof source[key] === CE9178">'object' && source[key] !== null) {
        target[key] = secureMerge(target[key] || {}, source[key]);
      } else {
        target[key] = source[key];
      }
    }
  }
  return target;
}

This approach is significantly safer than standard library implementations that don't check for these specific keys. For production-grade secure coding, I recommend using Object.create(null) for objects that store user-provided data. These objects don't have a prototype, effectively neutralizing the attack vector because they don't inherit from the base Object.

Beyond Merging: Preventing Object Injection

While merging is the most common culprit, data sanitization must happen at the entry point. If you’re accepting JSON in an Express route, validate it with a library like ajv before it reaches your business logic.

If you are dealing with more complex data structures, remember that Insecure Deserialization: How to Secure Object Hydration in Node.js and PHP is a related risk that often stems from similar flaws in how we handle incoming data. When you treat objects as opaque blobs, you lose control over what they contain.

I’ve also found that keeping your dependencies updated is non-negotiable. Many older versions of popular utility libraries like lodash or yargs had well-documented vulnerabilities regarding prototype pollution. Using npm audit or tools like snyk helps catch these before they reach production.

Practical Steps for Security

  1. Use Object.create(null): When initializing dictionaries or configuration maps, avoid the standard {} literal.
  2. Freeze the Prototype: In your application entry point, add Object.freeze(Object.prototype). This prevents any runtime modification of the base prototype, though it might break some older, poorly written dependencies.
  3. Validate Schemas: Never trust the shape of an incoming JSON object. Validate it early using a schema validator.
  4. Audit Dependencies: Regularly check for known CVEs in your node_modules.

If you are concerned about other injection vectors, remember that Preventing Prototype Pollution in Node.js: A Security Guide covers the recursive nature of these attacks in more detail. I've personally seen cases where a simple merge vulnerability led to privilege escalation because the application logic relied on an object property that was overwritten by an attacker.

Final Thoughts

Prototype pollution isn't just a theoretical bug; it’s a direct path to remote code execution in many Node.js environments. While I’m currently comfortable with the isSafeKey approach, I’m still cautious about how deep-merging logic interacts with newer JavaScript features like Proxy or Reflect.

Next time I build a data-heavy application, I’ll likely move toward immutable data structures entirely. It adds complexity, but it eliminates the entire class of prototype-based vulnerabilities. Until then, I’ll keep freezing my prototypes and validating every bit of incoming JSON.

Frequently Asked Questions

Is JSON.parse safe from prototype pollution? JSON.parse itself is safe, but the resulting object is not. The vulnerability occurs when your code iterates over the parsed object and merges it into an existing state or configuration object.

Does Object.freeze(Object.prototype) affect my code? It might. Some legacy libraries rely on monkey-patching the prototype. Test thoroughly in your staging environment before applying this globally.

Should I use a library to handle deep merging? Only if the library explicitly states it is protected against prototype pollution. Even then, verify their security disclosures. It’s often safer to write a simple, purpose-built recursive merge function that you understand completely.

Back to Blog

Similar Posts

SecurityJune 28, 20264 min read

Node.js Security: Fixing Unhandled Promise Rejections and Async Errors

Node.js security relies on robust asynchronous error handling. Learn to prevent unhandled promise rejections and state corruption in your backend services.

Read more
SecurityJune 25, 20264 min read

Preventing DOM-based XSS: A Guide for Modern JavaScript Apps

Learn how to stop DOM-based XSS by securing your client-side sinks and sources. Master practical input sanitization and secure coding techniques today.

Read more
SecurityJune 28, 20265 min read

Resource Locking: How to Prevent Deadlocks and DoS in Node.js & PHP

Master resource locking to prevent deadlocks and DoS attacks in your Node.js and PHP applications. Learn practical strategies for safe concurrency control.

Read more