Javascript Prototype
자바스크립트는 프로토타입(Prototype) 기반 언어라고도 불린다.
자바스크립트는 객채지향 언어이지만 C++이나 Java처럼 class가 없다.
대신 프로토타입이라는 개념이 등장한다.
1. Prototype vs Class
클래스(class)라는 개념은 객체지향 언어에서 빠질 수 없는 개념이다.
하지만 자바스크립트에는 클래스라는 개념이 없다.
대신 프로토타입(Prototype)이라는 개념이 존재한다.
클래스가 없으니 기본적으로 상속이 불가능하다.
그래서 보통 프로토타입 기반으로 상속을 흉내내도록 구현해 사용한다.
최근의 ECMA6 표준에서는 Class 문법이 추가되었습니다.
하지만 문법이 추가되었다는 것이지, 클래스 기반으로 바뀌었다는 것은 아닙니다.
2. 사용법
자바스크립트에 클래스는 없지만 함수(Function)와 new를 통해
클래스를 비슷하게 흉내 낼 수 있다.
function Person() {
this.eyes = 2
this.nose = 1
}
var p1 = new Person();
var p2 = new Person();
console.log(p1.eyes); // 2
console.log(p1.nose); // 1
console.log(p2.eyes); // 2
console.log(p2.nose); // 1
p1과 p2는 공통적으로 eyes와 nose를 공통적으로 가지고 있는데,
메모리에는 eyes와 nose가 2 개씩 총 4 개 할당된다.
객체를 1000개 만들면 200개의 변수가 메모리에 할당된다.
이런 문제를 프로토타입으로 해결할 수 있다.
function Person() { }
Person.prototype.eyes = 2;
Person.prototype.nose = 1;
var p1 = new Person();
var p2 = new Person();
console.log(p1.eyes); // 2
console.log(p1.nose); // 1
console.log(p2.eyes); // 2
console.log(p2.nose); // 1
Person.prototype이라는 빈 Object가 어딘가에 존재하고,
Person 함수로부터 생성된 객체(p1, p2)는
어딘가에 존재하는 Object에 들어있는 값을 모두 갖다 쓸 수 있다.
즉, eyes와 nose를 어딘가에 있는 빈 공간에 넣어두고
p1과 p2가 공유해서 사용하는 것이다.
3. Javascript Prototype Chain
앞에서 언급한 것처럼, 자바스크립트는 상속을 Prototype이라는 객체를 사용해서 구현한다.
사실 이미 상속이 된 객체를 사용하고 있다.
자바스크립트에서 객체의 부모는 __proto__를 통해 접근할 수 있다.
다른 프로그래밍 언어의 상속에서 그렇듯, 자식 객체에서 어떠한 변수를 찾을 수 없으면
부모 객체에서 해당 변수를 찾게 되는데, 자바스크립트에서는 이것을 Prototype Chain이라고 부른다.
let user = {
name: 'kim',
age: 20,
}
console.log(user.hasOwnProperty('name')); // true
user라는 객체에서 hasOwnProperty() 메서드를 선언하지 않았음에도 호출할 수 있는 이유는
user 객체가 부모 객체에서 hasOwnProperty() 메서드를 상속받았기 때문이다.
Object 리터럴 (즉, 중괄호 { }를 통한) 객체 선언 방식은 내부적으로 new Object();이 실행되며 선언되는데
이러한 이유로 Object 객체를 상속받게 되는 것이다.
var a = {
attr1: 'a1'
}
var b = {
attr2: 'a2'
}
b.__proto__ = a;
b.attr1 // 'a1'
b에서 부모 객체인 a의 attr에 접근할 수 있다. a가 부모 객체가 된 것이고, b가 자식 객체가 된 것이다.
객체 리터럴을 통해 선언한 객체의 부모는 Object.prototype 이기 때문에,
객체에서 undefined인 속성에 접근할 때 Object.prototype에도 해당 속성이 있는지 확인한다.
4. Javascript Prototype Pollution
자바스크립트 내부에서 객체지향의 핵심 기술인 상속을 Prototype Chain으로 구현한 점을 이용해,
특정 로직을 우회하거나 코드가 해커가 원하는 방향으로 실행되도록 만드는 공격이다.
let foo = {bar: 1};
let user = {
name: 'Kim',
age: 20,
}
foo.__proto__.isAdmin = true; // exploit
if (user.isAdmin) {
console.log(`${user.name} is admin`); // console.log() will be executed
}
속성 설정
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
객체 병합
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
객체 복사
function clone(obj) {
return merge({}, obj);
}
const obj1 = JSON.parse('{"__proto__":{"polluted":1}}');
const obj2 = clone(obj1);
const obj3 = {};
console.log(obj3.polluted); // 1
'Wargame - 웹' 카테고리의 다른 글
Dreamhack - crawling (0) | 2023.10.20 |
---|---|
Tomcat Manager (0) | 2023.10.18 |
[wargame.kr] fly me to the moon (0) | 2023.05.07 |
[wargame.kr] md5 password (0) | 2023.05.05 |
baby-sqlite (0) | 2023.05.03 |