프로토타입 오염(prototype pollution)은 공격자가 __proto__나 constructor.prototype 같은 특수 키를 입력에 넣어 Object.prototype을 바꾸는 JavaScript 취약점이다. 모든 객체가 프로토타입 체인(prototype chain)으로 그 속성을 물려받으므로, 애플리케이션 전체의 동작이 바뀐다.
function merge(target, source) { // 취약한 재귀 병합
for (const key in source) {
if (typeof source[key] === 'object') {
target[key] ??= {}
merge(target[key], source[key])
} else {
target[key] = source[key]
}
}
}
merge({}, JSON.parse('{"__proto__": {"isAdmin": true}}'))
const user = {}
user.isAdmin // true — 모든 객체가 오염됨JSON.parse는 __proto__를 평범한 키로 만들지만, 병합 함수가 target["__proto__"]에 접근하는 순간 실제 프로토타입을 건드린다.
주로 생기는 곳
lodash.merge같은 깊은 병합·복제 함수(구버전), 직접 짠 재귀 병합- 쿼리 문자열(
?__proto__[isAdmin]=1)이나 JSON 본문을 검증 없이 객체에 펼칠 때 Object.assign은 얕은 복사라 직접적으로는 덜 위험하지만, 재귀 로직과 섞이면 문제가 된다(얕은 복사와 깊은 복사)
방어
- 병합·할당 시
__proto__,constructor,prototype키를 건너뛴다 - 키-값 저장에는
Object.create(null)(프로토타입 없는 객체)이나Map을 쓴다 - 입력을 스키마로 검증해 허용한 필드만 받는다
- 라이브러리를 최신으로 유지한다. 주요 라이브러리는 이미 패치됐다
- 필요하면
Object.freeze(Object.prototype)으로 막을 수 있지만 다른 코드와 충돌할 수 있다
영향
서버(Node.js)에서는 권한 우회를 넘어 템플릿 엔진·자식 프로세스 옵션과 엮여 원격 코드 실행(Remote Code Execution, RCE)까지 이어진 사례가 있고, 브라우저에서는 XSS(Cross-Site Scripting)와 결합된다. 프로토타입 체인의 동작 자체는 믹스인에서 다루는 JavaScript 객체 모델과 같은 뿌리다.