Home Prototype Pollution
Post
Cancel

Prototype Pollution

Prototype Pollution에 대해서 알아봅시다.


Background

Javascript의 객체 지향

  • Javascript는 객체 지향 언어
  • 객체지향언어의 class 개념이 javascript에는 존재하지 않음 → 상속 기능을 사용하지 못한다는 의미
  • prototype이라는 고유 특성을 이용해 상속 기능을 구현
  • ECMA6 표준에서 class 키워드가 추가되었지만 궁극적으로 javascript에서 class기반의 객체지향 언어로 바뀌지는 않음
1
2
3
4
5
6
7
let user = {
	name: scyoon,
	age: 20,
}

console.log(user.hasOwnProperty(name)); // true
[참고] https://poiemaweb.com/js-prototype

user라는 객체에서 hasOwnProperty() 메소드를 선언하지 않았음에도 호출할 수 있는 이유는 user 객체의 부모 객체에서 hasOwnProperty() 메소드를 상속받았기 때문입니다.

Obeject literal 객체 선언 방식은 내부적으로 new Object(); 가 실행 되며 선언되므로 object 객체를 상속받게됩니다.


Javascript Prototype Chain

  • Javascript에서 객체의 부모는 __proto__ 로 접근이 가능함
  • 자식 객체에서 변수를 찾을 수 없으면 부모 객체에서 해당 변수를 찾게 되는데, Javascript에서는 Prototype Chain 이라고 부름

Prototype

  • Javascript에는 객체라는 하나의 구조만 존재
  • 각 객체에는 Prototype이라는 다른 객체에 대한 링크를 보유하는 비공개 속성
  • Prototype 객체도 자신만의 Prototype을 가지고 있음
  • Prototype으로 null을 가진 객체에 도달할 때 까지 연결이 유지됨,
  • null에는 prototype이 없으며, prototype chain에서 최종 링크 역할을 함

prototype

Prototype Pollution

기본적으로 Object literal(객체 리터럴)의 __proto__ == Object.prototype 과 같다는 것을 이용해 다른 객체의 속성에 영향을 줄 수 있습니다.
prototype chain으로 특정 로직을 우회하거나 공격자가 원하는 방향으로 실행되도록 만드는 공격 기법입니다.

Object literal

literal이란 사람이 이해할 수 있는 문자나 약속된 기호를 사용해 값을 생성하는 표기법이며, JS에서 객체를 생성하는 가장 일반적인 방법이 객체 리터럴을 사용합니다.
객체 리터럴은 {} 내에 0개 이상의 property(객체의 상태를 나타내는 값)를 정의합니다.

Prototype Pollution Pattern

Prototype Pollution 패턴에는 보통 세 가지 패턴(속성 설정, 객체 병합, 객체 복사)이 존재합니다.

Setting Property

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function isObject(obj) {
  return obj !== null && typeof obj === 'object';
}

function setValue(obj, key, value) {
  const keylist = key.split('.');
  const e = keylist.shift();
  if (keylist.length > 0) {
    if (!isObject(obj[e])) obj[e] = {};
    setValue(obj[e], keylist.join('.'), value);
  } else {
    obj[key] = value;
    return obj;
  }
}

const obj1 = {};
setValue(obj1, "__proto__.polluted", 1);
const obj2 = {};
console.log(obj2.polluted); // 1

Object Merge

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function merge(a, b) {
  for (let key in b) {
    if (isObject(a[key]) && isObject(b[key])) {
      merge(a[key], b[key]);
    } else {
      a[key] = b[key];
    }
  }
  return a;
}

const obj1 = {a: 1, b:2};
const obj2 = JSON.parse('{"__proto__":{"polluted":1}}');
merge(obj1, obj2);
const obj3 = {};
console.log(obj3.polluted); // 1

Object Copy

1
2
3
4
5
6
7
8
function clone(obj) {
  return merge({}, obj);
}

const obj1 = JSON.parse('{"__proto__":{"polluted":1}}');
const obj2 = clone(obj1);
const obj3 = {};
console.log(obj3.polluted); // 1

Mitigation

이 공격을 방지하는 대책으로는 3가지 방법이 있습니다.

  • Object.freeze : Object.prototype이나 Object를 freeze하여 변경을 불가능하게 하는 방법.
    –> 부작용으로 정상적인 모듈임에도 이 조치로 동작하지 않을 수 도 있습니다.
  • JSON schema : avj 모듈 등을 사용해 JSON을 검증
  • Map : key/value를 저장하는 객체를 사용하지 않고 Map을 사용. ES5 이전 환경에서는 불가

Reference

https://ufo.stealien.com/2020-12-23/javascript-prototype-pollution
https://velog.io/@ehgks0000/Prototype-Pollution