빠른 흐름
신뢰할 수 없는 JSON 처리
1. Content-Type과 body 크기를 먼저 확인한다
2. JSON parser로 파싱하고 eval은 쓰지 않는다
3. 최대 크기, 중첩 depth, array 길이를 제한한다
4. schema나 명시적 allowlist로 구조를 검증한다
5. __proto__, constructor, prototype 같은 위험 key를 merge 전에 차단한다방어 기준
JSON은 데이터 형식이지 실행 코드가 아니다
외부에서 받은 JSON 문자열을 eval()로 처리하면 데이터 파싱이 아니라 코드 실행 문제가 됩니다. JSON parser를 사용하면 문법적으로 허용된 JSON text만 값으로 바뀌고, 실패 시 파싱 오류로 다룰 수 있습니다. 보안 경계에서는 "문자열을 실행하지 않는다"가 첫 번째 기준입니다.
파싱 성공도 충분한 검증은 아닙니다. { "role": "admin" }은 유효한 JSON이지만 현재 endpoint가 받을 수 있는 payload인지는 별도 계약 문제입니다. 파싱 후에는 schema 검증이나 allowlist 기반 필드 검증이 필요합니다.
크기와 depth 제한이 없으면 parser 자체가 부담이 된다
JSON은 텍스트 형식이라 매우 큰 array, 깊은 중첩 object, 긴 문자열을 만들기 쉽습니다. parser가 입력을 모두 메모리에 올리는 구현이라면 작은 API endpoint도 큰 body 하나로 메모리와 CPU를 많이 사용할 수 있습니다. HTTP layer에서 body size limit을 두고, 애플리케이션 layer에서 depth와 array 길이, string 길이 제한을 별도로 확인하는 편이 안전합니다.
제한할 지점
- 요청 body byte 크기
- object/array 중첩 depth
- array 최대 item 수
- string 최대 길이
- 한 요청에서 처리할 document 수merge 전에는 위험 key를 차단한다
JavaScript 환경에서는 JSON으로 들어온 object를 설정 object나 prototype이 있는 object에 깊게 merge할 때 prototype pollution 위험이 생길 수 있습니다. __proto__, constructor, prototype 같은 key는 일반 데이터처럼 보이지만 merge 로직에 따라 object prototype에 영향을 줄 수 있습니다. 외부 JSON을 내부 설정과 합치기 전에는 allowlist로 허용 key를 제한하거나, prototype 없는 object와 안전한 merge 함수를 사용합니다.
어디서 막을까
| 위험 | 방어 위치 |
|---|---|
| 잘못된 media type | HTTP header 확인 |
| 너무 큰 body | reverse proxy 또는 server body limit |
| 깊은 중첩과 긴 array | parser 전후 size/depth 제한 |
| 예상 밖 field | JSON Schema 또는 allowlist |
| 위험 key merge | merge 전 key 필터링 |
| HTML 에러를 JSON으로 파싱 | 응답 Content-Type 확인 |
주의할 점
JSON parser를 통과했다는 것은 문법이 맞다는 뜻일 뿐입니다. 보안 경계에서는 문법 검증, 구조 검증, 크기 제한, merge 안전성 검사를 별도 단계로 나눠야 합니다.
const input = JSON.parse(text);
// 위험: 외부 입력을 그대로 설정 object에 깊게 merge
deepMerge(config, input);외부 JSON을 내부 object에 합칠 때는 허용 key만 남기고, prototype 관련 key를 차단한 뒤 merge해야 합니다.
참고 링크
2 sources