설명
외부 입력으로 객체를 선택하고 그 속성에 값을 쓰면, __proto__ 같은 키를 통해 공유 프로토타입에 접근할 수 있습니다. 오염된 프로토타입을 상속하는 객체에서 의도하지 않은 속성이 읽히며, 이를 사용하는 애플리케이션 로직에 따라 보안 문제가 발생할 수 있습니다.
잠재적 영향
- 오염된 속성을 사용하는 기능이 예상과 다르게 동작할 수 있습니다.
- 데이터 처리나 설정이 바뀌어 데이터가 손상될 수 있습니다.
- 인증·인가 판단이 오염된 값에 의존하면 검증이 우회될 수 있습니다.
해결 방법
- 최종 필드명뿐 아니라 중간 객체를 선택하는 키도 검증하고,
__proto__,constructor,prototype을 차단하세요. - 변경 가능한 필드는 서버가 소유한 허용 목록으로 제한하세요. 요청으로 전달받은 허용 목록은 신뢰하지 마세요.
Object.hasOwn으로 상속 속성을 구분하고, 사전 형태의 저장소에는Object.create(null)이나Map을 고려하세요.
예시
변경 전
javascript
app.get('/test/:id', (req, res) => {
let id = req.params.id;
let items = req.session.todos[id];
if (!items) {
items = req.session.todos[id] = {};
}
// 위험: 외부 입력으로 바로 키를 사용
items[req.query.name] = req.query.text;
res.sendStatus(200);
});
변경 후
javascript
app.get('/test/:id', (req, res) => {
const id = req.params.id;
const blockedKeys = new Set(['__proto__', 'constructor', 'prototype']);
const allowedKeys = new Set(['title', 'text', 'done']);
if (blockedKeys.has(id) || !allowedKeys.has(req.query.name)) {
return res.status(400).send('Invalid key name');
}
if (!Object.hasOwn(req.session.todos, id)) {
req.session.todos[id] = Object.create(null);
}
const items = req.session.todos[id];
items[req.query.name] = req.query.text;
res.sendStatus(200);
});
설명:
- 변경 전:
id가__proto__이면 공유 프로토타입을 선택할 수 있고, 검증하지 않은 필드명으로 값을 씁니다. - 변경 후: 객체 선택 키와 최종 필드를 모두 검사하고, 상속 속성을 기존 항목으로 취급하지 않습니다. 세션의
todos저장소가 초기화되어 있다고 가정합니다.