localStorage + getter = Prototype Pollution
오늘은 Prototype Pollution에 대한 이야기를 잠깐 하려고 합니다. 다름이 아니라 @garethheyes가 아래와 같은 내용의 트윗을 올렸었습니다.
정리하면 localStorage 에서 getter를 사용하는 경우, 즉 직접 접근해서 사용하는 경우 Prototype Pollution에 취약할 수 있다 란 이야기죠. 물론 당연한 이야기지만 글로 한번 공유하는게 좋을 것 같아 남겨봅니다.
Prototype Pollution
Prototype Pollution은 Javascript 처리 로직의 문제로 Object 들의 prototype을 수정할 수 있을 때 발생하는 보안 문제를 의미합니다. XSS 관련 문제에서 자주 나오며, 실제로 서비스 사레에서도 종종 나오기도합니다.
공격에 대한 자세한 내용은 Cullinan > Prototype Pollution 문서를 참고해주세요!
const obj2 = JSON.parse('{"__proto__":{"hacked":45}}');
merge(obj1, obj2);
const obj3 = {};
obj3.hacked; // 45
localStorage and getter
localStorage에서 데이터를 꺼내는 방법은 여러가지가 있습니다. 보통 get() 함수를 사용하지만 종종 getter로 호출해서 꺼내는 경우가 있습니다.
// get() 함수 사용
localStorage.get('testdata')
// getter 사용
localStorage.testdata
Pollution!
localStorage 또한 Object이고 merge나 queryParse 등으로 Pollution이 일어나는 경우 Object 하위 요소를 임의로 변경할 수 있어, 결국 localStorage에 저장된 데이터를 getter로 꺼내는 경우 Pollution된 내용이 먼저 사용되기 떄문에 임의로 값을 변경할 수 있게됩니다.
하나 간단하게 예시를 들어보면 제 Firefox에는 StackColorScheme란 값이 하나 저장되어 있습니다.
예전에 제가 darkmode toggle 테스트한다고 임의로 넣어둔 값이였네요
값은 dark 라는 값이 들어있습니다.
localStorage.StackColorScheme
// => "dark"
이 때 Prototype Pollution 을 통해 Object의 prototype 중 StackColorScheme 값을 변경했을 때 localStorage.StackColorScheme 와 같이 getter로 값을 꺼내는 경우 pollution 된 값을 읽어오게 됩니다.
Object.prototype.StackColorScheme = "hahwul!"
localStorage.StackColorScheme
// => "hahwul!"
실제론 아래와 같은 케이스가 가장 흔하겠네요.
// localStorage.mydebug is false
// Pollution!
const obj2 = JSON.parse('{"__proto__":{"mydebug":true}}');
merge(obj1, obj2);
// localStorage.mydebug is true
if localStorage.mydebug {
// some debug logic..
}