Depending on the application and runtime environment, prototype pollution may lead to security control bypasses, unexpected application behaviour, denial of service or, in certain server-side scenarios, remote code execution when combined with additional weaknesses.
Primary risk: Unintended modification of JavaScript object properties through prototype manipulation.
Common targets: Node.js applications, JavaScript libraries, configuration processors and web applications that merge untrusted objects.
Potential impact: Authorisation bypass, application logic manipulation, denial of service and possible code execution.
Key investigation evidence: Suspicious object keys, unsafe merge operations, unexpected inherited properties and anomalous application behaviour.
Primary defences: Validate object keys, use safe data structures, avoid unsafe merges and update vulnerable dependencies.
1. When One Object Affects Others
Imagine a Node.js application that accepts user preferences through an API. Users can customise settings such as language, theme and notification preferences.
The application receives a JSON object and merges it into an existing configuration object.
Under normal circumstances, this is a routine operation. The application expects users to modify only the settings intended for them.
However, JavaScript objects can inherit properties from other objects through a mechanism called the prototype chain. If an application processes specially crafted property names through an unsafe merge operation, an attacker may be able to modify a shared prototype or influence properties inherited by other objects.
The consequences can extend beyond the original input.
For example, application code may check whether a property is truthy and assume that it was explicitly configured. If prototype pollution introduces that property through inheritance, the application may behave differently from what the developer intended.
This vulnerability is known as Prototype Pollution.
The important distinction is that modifying one ordinary object's property is not necessarily prototype pollution. The security concern arises when attacker-controlled input changes prototype-related behaviour or causes unexpected inherited properties to influence application logic.
2. Understanding JavaScript Prototypes
JavaScript uses prototypes to support inheritance. An object can inherit properties and methods from another object, allowing multiple objects to share functionality.
Consider this simplified example:
const user = {
name: "Alex"
};
console.log(user.name); // Alex
console.log(user.theme); // undefined
The user object has a name property but no own theme property.
JavaScript property lookup can also follow the object's prototype chain. If a property is not found directly on the object, the runtime may look for it on the object's prototype.
This behaviour is fundamental to JavaScript and is not inherently insecure.
The risk appears when untrusted input can influence prototypes or introduce unexpected inherited properties.
The role of Object.prototype
Many ordinary JavaScript objects inherit from Object.prototype, which provides commonly available properties and methods.
If a shared prototype is modified, many objects that inherit from it may observe the change.
This can create a wider impact than modifying a single object's own properties.
However, not every object inherits from Object.prototype. Objects created with a null prototype, certain built-in objects and other specialised structures may behave differently.
3. How Prototype Pollution Happens
Prototype pollution often originates in code that recursively merges objects or copies arbitrary property names from untrusted input.
Commonly affected patterns include:
Deep-merge utilities.
Recursive configuration updates.
Generic object-assignment functions.
Request-body processing.
Query-string parsers.
Object path setters.
Third-party libraries that manipulate nested JavaScript objects.
The vulnerability frequently involves special property names such as __proto__, constructor and prototype.
These names are not automatically malicious in every context. Their significance depends on how the application interprets them and whether they can influence prototype-related operations.
A simplified vulnerable design
Consider a function that copies properties from user-controlled data into an application configuration:
function updateConfig(config, input) {
for (const key of Object.keys(input)) {
config[key] = input[key];
}
}
This code illustrates a generic property-copying pattern. It is not automatically exploitable as prototype pollution in every JavaScript runtime or configuration.
The risk increases when an application recursively processes nested objects, interprets special property names or passes the input to an unsafe merge or path-setting function.
A superficial check of the top-level property names may not be sufficient if nested objects are processed without equivalent validation.
Security lesson: Treat user-controlled object structures as untrusted data, especially when recursive operations or prototype-sensitive property names are involved.
4. Common Prototype Pollution Sources
Deep object merging
Applications often merge default settings with user-supplied preferences.
If a merge utility recursively processes nested objects without safely handling prototype-related keys, attacker-controlled input may influence shared object behaviour.
The impact depends on the exact merge implementation and how it handles special property names.
Query-string parsing
Some query parsers support nested parameters and convert them into JavaScript objects.
If nested paths can reference prototype-related properties and the parser does not handle them safely, the resulting object may create a pollution risk.
Configuration processing
Applications may load configuration from API requests, environment-derived data, uploaded files or external integrations.
Unsafe merging or property-path assignment can allow untrusted configuration values to affect unrelated application logic.
Vulnerable dependencies
A third-party package may contain an unsafe recursive merge, object-path setter or parser.
If the application uses the vulnerable function with attacker-controlled data, the dependency may create an exploitable path even when the application's own code appears simple.
Client-side data processing
Prototype pollution can also affect browser-based applications. Vulnerable libraries may process URL parameters, form data or imported objects.
Client-side impact depends on whether the polluted properties influence security-sensitive browser logic, rendering, DOM manipulation or other application functionality.
5. From Prototype Pollution to Application Compromise
Prototype pollution is a vulnerability class with multiple possible outcomes. The presence of pollution does not automatically mean remote code execution is possible.
Stage 1: Control over object properties
The attacker finds a path through which specially crafted input reaches a vulnerable object-processing operation.
Stage 2: Unexpected inherited properties
The operation modifies a relevant prototype or causes properties to be inherited where the application expects explicit values.
Stage 3: Application logic changes
Application code may use inherited properties when evaluating options, permissions, feature flags or configuration values.
This can lead to unexpected behaviour or, where security checks are incorrectly implemented, a potential authorisation bypass.
Stage 4: Dangerous functionality is influenced
In some applications, polluted properties can affect options passed to other libraries or influence functions that process commands, templates, requests or other sensitive operations.
The exact impact depends on the application's data flow and the functions that consume the affected properties.
Stage 5: Potential code execution
In certain server-side environments, prototype pollution can combine with an additional vulnerable code path or gadget to produce code execution.
This is not an automatic consequence of pollution. It requires a suitable execution path and the necessary environmental conditions.
Stage 6: Possible follow-on activity
If code execution is achieved, an attacker may attempt to access application secrets, modify files, initiate outbound connections or interact with other services available to the application process.
These activities must be investigated independently and should not be assumed from prototype pollution alone.
6. Investigating Prototype Pollution in a SOC
Consider a hypothetical Node.js application that receives an API request containing nested configuration data. Shortly afterward, the application begins producing unexpected results in feature checks, and an EDR alert reports unusual child-process activity.
The SOC team must determine whether the events are connected and whether the application has been exploited.
Step 1: Identify the entry point
Review web server, API gateway, application and reverse proxy logs.
Look for:
Requests containing nested object structures.
Prototype-sensitive property names.
Repeated requests with unusual JSON or query-string parameters.
Input directed at configuration, import or preference-update endpoints.
Unexpected application errors following suspicious requests.
Preserve the original request data when available. Avoid assuming that every occurrence of __proto__, constructor or prototype represents an attack; legitimate code and application data may contain these strings.
Step 2: Identify the affected code path
Work with the application team to determine whether the request reaches:
A deep-merge utility.
A recursive property-copying function.
A query-string or body parser.
An object-path setter.
A configuration-processing library.
Review source code, dependencies and relevant versions.
Establish whether the application processes nested keys and whether prototype-sensitive keys are rejected or safely handled.
Step 3: Assess prototype state
In a controlled investigation environment, examine whether unexpected properties have appeared on shared prototypes or are being inherited by application objects.
Where source-level debugging is available, distinguish between an object's own properties and inherited properties.
For example, developers can inspect property ownership using methods such as Object.hasOwn() and examine prototype relationships using appropriate runtime debugging facilities.
Avoid making uncontrolled changes to production prototypes during an investigation. Instrumentation and testing should be designed to minimise service disruption.
Step 4: Review application behaviour
Determine which parts of the application consume the affected properties.
Investigate unexpected changes to:
Configuration values.
Feature flags.
Access-control decisions.
Request-handling options.
Template or rendering settings.
Library behaviour.
An inherited property is not inherently a vulnerability. The key question is whether attacker-influenced inherited data changes a security-sensitive operation.
Step 5: Correlate endpoint and network activity
If there is evidence of possible server-side exploitation, review:
Process creation by the Node.js runtime.
Command-line arguments.
File modifications.
DNS queries and outbound connections.
EDR alerts.
Application restarts and crashes.
Access to environment variables or secrets, where observable.
Correlate these events with the suspicious request and the affected application instance.
Step 6: Determine scope and impact
Identify the affected package versions, endpoints, services and deployment instances.
Establish whether the issue was limited to altered application behaviour or whether there is evidence of information disclosure, privilege misuse, denial of service or code execution.
Do not classify a prototype-pollution finding as remote code execution unless the evidence supports that conclusion.
7. Evidence and Indicators to Review
Prototype pollution does not have a universal indicator of compromise. Detection requires a combination of input analysis, code-path understanding and behavioural telemetry.
Evidence sourceWhat to investigateWAF and API logsPrototype-sensitive keys and unusual nested parametersApplication logsUnexpected configuration values, exceptions and logic changesSource codeUnsafe recursive merges and property-path assignmentDependency inventoryVulnerable merge, parser and object-manipulation librariesRuntime instrumentationUnexpected inherited properties or prototype changesEDR telemetryUnusual child processes or file activity associated with the applicationNetwork logsUnexpected outbound connections following suspicious requestsDeployment recordsDependency updates, new endpoints or configuration changes
Patterns that may warrant investigation
Repeated nested inputs containing prototype-sensitive property names.
A request followed by unexpected inherited properties.
Configuration values changing without a corresponding authorised update.
Security checks behaving differently after a suspicious API request.
An application process initiating unusual file or network activity after a suspected pollution event.
Errors or crashes associated with a vulnerable object-processing function.
These patterns are investigative leads rather than definitive proof. The strongest finding connects the suspicious input to an unsafe code path and demonstrates its effect on application behaviour.
8. Detection Engineering and MITRE ATT&CK
A useful detection strategy combines request-level signals with knowledge of vulnerable dependencies and unexpected application behaviour.
Example detection logic
Identify endpoints that accept nested JSON, query parameters or user-defined configuration.
Detect suspicious prototype-related keys in contexts where they are not expected.
Correlate requests with application exceptions or unexpected configuration changes.
Enrich alerts with the affected package, version and application component.
Review process, file and network telemetry for evidence of follow-on exploitation.
Prioritise events where application behaviour changes or additional compromise indicators are observed.
Avoid alerting on every occurrence of __proto__ or constructor without context. These strings may appear in legitimate development tools, libraries, tests and application data.
MITRE ATT&CK mapping
Prototype pollution is a vulnerability class rather than a standalone ATT&CK technique. The appropriate mapping depends on the behaviour established by the investigation.
T1190 — Exploit Public-Facing Application: Relevant when an attacker exploits a vulnerability in an internet-facing application.
T1059 — Command and Scripting Interpreter: Potentially relevant if the incident includes command or scripting interpreter execution.
T1499 — Endpoint Denial of Service: Potentially relevant when the observed behaviour involves denial of service against an endpoint, subject to the specific circumstances.
Do not map all these techniques to every prototype-pollution alert. Record only the behaviours supported by evidence.
9. Preventing Prototype Pollution
The strongest defence is to prevent untrusted input from modifying prototypes and to avoid application designs that rely on inherited properties for security-sensitive decisions.
Validate object keys
Reject or safely handle prototype-sensitive keys in untrusted input where they are not required.
Validation should apply recursively to nested objects, not just to the top-level request.
Use safe object-processing patterns
Avoid unsafe recursive merge and object-path operations on attacker-controlled data.
Use well-maintained libraries with documented security behaviour, and review the exact functions used to process external input.
Prefer safe data structures
Where appropriate, use objects with a null prototype for dictionaries that do not need inherited properties, or use Map for key-value collections.
For example:
const settings = Object.create(null);
settings["theme"] = "dark";
An object created with Object.create(null) does not inherit from Object.prototype. This can reduce certain prototype-related risks when used appropriately.
However, changing the data structure alone does not eliminate every prototype-pollution vulnerability elsewhere in an application.
Check own properties explicitly
When a security-sensitive decision depends on whether a value was explicitly provided, distinguish own properties from inherited ones.
For example:
if (Object.hasOwn(config, "isAdmin")) {
// Handle an explicitly defined property.
}
This pattern can prevent some inherited-property mistakes, but it is not a substitute for proper authorisation checks or safe object processing.
Update vulnerable dependencies
Maintain an inventory of JavaScript packages and track security advisories affecting object-merging, parsing and property-manipulation utilities.
Upgrade vulnerable packages to supported fixed versions and verify that the vulnerable code path is no longer reachable.
Enforce explicit authorisation
Do not make access-control decisions solely on the basis of client-supplied configuration or inherited object properties.
Perform authorisation checks using trusted identity information and server-side policy.
Apply least privilege
Run Node.js services with minimal operating-system permissions and restrict their access to secrets, files and outbound network destinations.
These controls reduce potential impact if a prototype-pollution vulnerability is combined with another weakness.
Add security tests
Test object-processing functions with nested and prototype-sensitive inputs in a controlled environment. Include regression tests to ensure that shared prototypes remain unchanged and security-sensitive logic behaves correctly.
10. Incident Response Checklist
When prototype pollution is suspected:
Preserve evidence: Collect the original request, application logs, dependency information and relevant endpoint telemetry.
Identify the processing path: Determine which parser, merge function or library processed the input.
Confirm the behaviour: Establish whether a prototype was modified or inherited properties influenced application logic.
Assess the impact: Investigate authorisation changes, information exposure, service disruption or possible code execution.
Contain the vulnerable endpoint: Restrict or disable the affected functionality where necessary.
Review dependencies: Patch or replace vulnerable packages and remove unsafe object-processing patterns.
Investigate follow-on activity: Examine process execution, file modifications and network connections if compromise is suspected.
Validate the fix: Retest the affected code path and monitor for recurrence.
Conclusion
Prototype pollution illustrates how a small weakness in JavaScript object handling can affect application logic far beyond the original request. When untrusted input reaches unsafe object-processing operations, inherited properties may change how unrelated parts of an application behave.
The consequences range from unexpected configuration changes and authorisation mistakes to denial of service or, in certain server-side environments, code execution when additional weaknesses are present.
For SOC analysts, the challenge is to connect suspicious input with prototype changes and observable application behaviour. For developers, the priority is to use safe data structures, validate nested input, avoid unsafe merge operations and keep dependencies updated.
The key lesson: In JavaScript, data that influences an object's prototype can affect more than the object that received it.
Further Reading
OWASP — Prototype Pollution Prevention Cheat Sheet
PortSwigger Web Security Academy — Prototype Pollution
MDN Web Docs — Inheritance and the Prototype Chain
CWE-1321 — Improperly Controlled Modification of Object Prototype Attributes
Node.js documentation — Security Best Practices