노트

프로토타입 오염

Prototype Pollution

백엔드#security#js · 연결된 개념 5개

쉽게 말하면

프로토타입 오염은 전 직원이 참고하는 공용 규칙집에 '모든 사람은 관리자다'라는 한 줄을 몰래 끼워 넣는 거예요. 각자 따로 정하지 않은 항목은 규칙집을 따르니, 앱의 모든 객체가 그 값을 물려받죠.

비유가 깨지는 곳 규칙집에 직접 손대는 게 아니라 __proto__ 같은 특수 키가 든 입력을 재귀 병합할 때 일어나요. 이런 키를 건너뛰고, 저장엔 Object.create(null)이나 Map을 쓰고, 입력을 스키마로 검증해요.

프로토타입 오염(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 객체 모델과 같은 뿌리다.

출처: OWASP Prototype Pollution Prevention Cheat Sheet

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • 변수·컬렉션 캡슐화

    넓은 범위에서 쓰이는 데이터에 직접 접근하지 못하게 하고, 읽고 쓰는 함수를 통해서만 다루게 하는 리팩터링. 데이터를 옮기거나 바꿀 때 고쳐야 할 곳이 접근 함수 한 곳으로 좁아진다.

  • 기본형 집착

    전화번호·금액·우선순위 같은 도메인 개념을 끝까지 문자열이나 숫자로만 다루는 냄새. 같은 검증과 포맷 코드가 여기저기 반복되고, 문자열로 모든 걸 표현하는 "stringly typed" 코드가 된다.

  • 버전 스큐와 정적 자산 오프로드

    버전 스큐(Version Skew)는 새 버전을 배포한 뒤에도 브라우저에 떠 있는 이전 빌드의 클라이언트가 새 서버와 통신하면서 생기는 불일치다. 대표 증상은 이전 HTML이 참조하던 JS 청크를 늦게 불러오려다 404가 나는 ChunkLoadError다.

  • 언어적 안티패턴

    이름, 타입, 주석이 말하는 것과 코드가 실제로 하는 일이 어긋나는 것. 읽는 사람을 잘못된 추측으로 이끈다.

  • 매개변수 객체 만들기

    여러 함수에 늘 함께 넘겨지는 값 묶음(데이터 뭉치, Data Clumps)을 하나의 객체로 묶어 넘기는 리팩터링.

보기 옵션