---
title: "Property-Based Testing이 __proto__ 보안 버그를 잡아낸 이야기"
published: 2025-12-18T23:40:46.000Z
canonical: https://jeff.news/article/1014
---
# Property-Based Testing이 __proto__ 보안 버그를 잡아낸 이야기

Kiro IDE에서 localStorage 기반 API 키 저장 기능을 PBT로 테스트하다 75번째 반복에서 __proto__ 프로토타입 오염 버그를 발견. 유닛 테스트나 코드 리뷰로는 못 잡았을 엣지 케이스.

## 뭘 만들다가 발견했나

- Kiro(AWS의 AI IDE)로 채팅 앱의 스토리지 서비스를 구현하는 과정에서 발견된 실제 사례임. API 키를 provider 이름을 키로 `localStorage`에 저장/조회하는 단순한 기능이었음
- SDD(Specification-Driven Development) 워크플로우에 따라 요구사항을 정의하고, 테스트 가능한 속성을 추출하고, 코드를 구현했는데 — 코드 리뷰도 통과했을 법하고, 기존 유닛 테스트도 다 통과했음

## 75번째 반복에서 터짐

- **라운드트립 속성**을 테스트한 거임: 임의의 provider와 key로 저장 → 조회하면 같은 값이 나와야 한다는 것
- fast-check 라이브러리로 PBT(Property-Based Testing)를 돌렸더니, **75번째 반복에서 provider가 `"__proto__"`일 때 실패**함
- `__proto__`를 키로 객체에 값을 쓰면 JavaScript 엔진이 이걸 프로토타입 체인으로 해석해서 **문자열 값을 거부**하고, 나중에 읽으면 빈 객체 `{}`가 반환됨

## 왜 이게 보안 문제인가

- 현재 구현에서는 직접적으로 익스플로잇 불가능함 — 객체가 직렬화 후 바로 해제되고, `JSON.stringify`가 `__proto__` 필드를 건너뛰기 때문
- 하지만 **리팩토링 과정에서 새 코드 경로가 추가되면** 공격자가 프로토타입에 값을 주입해 이후 속성 읽기를 오염시킬 수 있는 **프로토타입 오염(prototype pollution)** 취약점으로 발전할 수 있음

> [!TIP]
> 수정은 간단함: `Object.create(null)`로 프로토타입 체인이 없는 객체를 만들면 `__proto__`가 그냥 일반 속성이 됨. 조회 시에도 `Object.hasOwn()`으로 안전하게 체크.

## PBT가 여기서 빛나는 이유

- 유닛 테스트는 작성자(사람이든 LLM이든)의 **편향(bias)**에 갇힘. 구현할 때 생각 못 한 엣지 케이스를 테스트할 때도 생각 못 함
- PBT는 fast-check 커뮤니티가 축적한 **"잘 알려진 버그 문자열"** 지식을 테스트 생성기에 인코딩해둔 거라서, `__proto__` 같은 함정을 자동으로 시도함
- `numRuns: 100`이 기본값인데, 이번 건은 75번째에서 잡힌 거임. 확신이 필요하면 숫자를 올리면 되고, 성능이 중요하면 낮추면 됨
- 속성(property)이 요구사항에서 직접 도출되기 때문에, **실행 가능한 명세(executable specification)** 역할을 함. 요구사항과 테스트 사이의 간극을 메워줌

## 핵심 포인트

- fast-check 라운드트립 테스트 75회차에서 __proto__ 키 실패
- JavaScript 프로토타입 체인이 문자열 할당을 거부하는 문제
- Object.create(null)로 수정
- PBT 생성기에 인코딩된 커뮤니티 지식이 편향을 극복

## 인사이트

PBT의 가치를 보여주는 교과서적 사례. LLM이 생성한 코드도 같은 편향을 가진다는 점에서 AI 코딩 시대에 더 중요해지는 테스트 방법론.
