Description
When user input controls a nested property path, following __proto__ or constructor.prototype can write to the prototype itself. Polluting a shared prototype may make attacker-supplied properties appear on other objects that inherit from it.
Potential impact
- Application logic that reads polluted properties may behave unexpectedly.
- Authentication or authorization checks may be bypassed if they rely on those properties.
- Modified functions or configuration values may cause failures or data corruption.
Remediation
- Check that every path component is a string, and block
__proto__,constructor, andprototype. - Restrict writable fields with a server-defined allow-list and avoid following inherited properties during traversal.
- Consider
Object.create(null)orMapfor dictionary-like storage. - Apply security patches to libraries such as Lodash or dot-prop. Using a library does not replace input validation.
Examples
Before
javascript
// Before: unrestricted dynamic property paths.
function setDeepValue(obj, keys, value) {
keys = Array.isArray(keys) ? keys : keys.split('.')
let cur = obj
while (keys.length > 1) {
const key = keys.shift()
// Before: continue even when key is '__proto__'.
if (!cur[key]) cur[key] = {}
cur = cur[key]
}
cur[keys[0]] = value
}
// setDeepValue(user, ['__proto__', 'isAdmin'], true);
After
javascript
// After: validate path components and use own properties.
function setDeepValueSafe(obj, keys, value) {
const blockedKeys = ['__proto__', 'constructor', 'prototype']
keys = Array.isArray(keys) ? [...keys] : keys.split('.')
if (keys.length === 0 || keys.some(key =>
typeof key !== 'string' || blockedKeys.includes(key))) {
throw new Error('위험한 속성명입니다.')
}
let cur = obj
while (keys.length > 1) {
const key = keys.shift()
if (!Object.hasOwn(cur, key)) cur[key] = Object.create(null)
cur = cur[key]
if (cur === null || typeof cur !== 'object') {
throw new Error('위험한 속성명입니다.')
}
}
cur[keys[0]] = value
}
Explanation:
- Before: Unrestricted traversal can reach a shared prototype through paths such as
__proto__and write to it. - After: Validate component types and dangerous keys first, then follow only the object's own properties at intermediate steps. New containers have no prototype.