YAML 보안 취약점 정리

개요

YAML은 "사람이 읽기 편한" 포맷을 목표로 설계되었지만, 그 과정에서 추가된 수많은 암묵적 규칙과 기능들이 오히려 보안 취약점의 원인이 되었다. 복잡한 DSL일수록 파서 구현이 어렵고 잘못 구성된 파서는 심각한 보안 문제를 야기한다.


주요 취약점

1. 역직렬화(Deserialization) 공격 — 원격 코드 실행(RCE)

YAML의 가장 치명적인 취약점. 많은 YAML 파서가 기본적으로 임의 객체를 역직렬화할 수 있어, 공격자가 YAML 파일을 통해 서버에서 코드를 실행시킬 수 있다.

공격 예시 (PyYAML 기본 설정)

# 파싱 시 os.system("rm -rf /") 실행됨
!!python/object/apply:os.system
- "rm -rf /"

Ruby (Psych 파서)

---
!ruby/object:Gem::Installer
i: !ruby/object:Gem::Package::TarReader
  io: !ruby/object:Net::BufferedIO
    io: !ruby/object:Gem::Package::TarReader::Entry
      read: 0
      header: "aa"

원인: yaml.load() 함수가 타입 태그(!!)를 통해 임의 Python/Ruby 객체를 인스턴스화함

대응책:

  • Python: yaml.load() ❌ → yaml.safe_load()
  • Ruby: YAML.safe_load() 사용
  • Java (SnakeYAML): new SafeConstructor() 사용

관련 CVE 및 참고자료:


2. 앨리어스 폭탄 (Billion Laughs / YAML Bomb)

XML의 "Billion Laughs" 공격과 동일한 원리. 앵커(&)와 앨리어스(*)를 중첩시켜 지수적으로 메모리를 소모시키는 DoS 공격.

공격 예시

a: &a ["lol", "lol", "lol", "lol", "lol", "lol", "lol", "lol", "lol"]
b: &b [*a, *a, *a, *a, *a, *a, *a, *a, *a]
c: &c [*b, *b, *b, *b, *b, *b, *b, *b, *b]
d: &d [*c, *c, *c, *c, *c, *c, *c, *c, *c]
e: &e [*d, *d, *d, *d, *d, *d, *d, *d, *d]
# 파싱 시 메모리 수 GB 소모 → 서비스 다운

원인: 앨리어스 확장 시 깊이/크기 제한이 없는 파서

대응책:

  • 앨리어스 확장 깊이 및 노드 수 제한 설정
  • 외부 입력 YAML에서 앨리어스 기능 비활성화

관련 CVE 및 참고자료:


3. 타입 강제 변환 (Type Coercion) — 예측 불가능한 파싱

YAML은 값의 타입을 자동으로 추론하는데, 이 과정에서 의도치 않은 값 변환이 발생한다.

문제 예시

# 개발자가 문자열을 의도했지만...
port: 080          # 8진수로 해석 → 64 (YAML 1.1)
enabled: yes       # true로 해석
api_key: 1234e5    # 부동소수점으로 해석 → 123400.0
version: 1.0       # float으로 해석 → 문자열 "1.0" 비교 실패
country_code: NO   # false로 해석 ← "Norway Problem"
country_code: ON   # true로 해석
date: 2024-01-15   # Date 객체로 해석

Norway Problem: 국가 코드 NO(노르웨이)가 false로 파싱되는 실제 버그. ISO 국가 코드 목록을 YAML로 관리할 때 발생.

대응책:

  • 모든 값에 명시적 따옴표 사용: country_code: "NO"
  • YAML 1.2 스펙을 지원하는 파서 사용 (타입 추론 규칙이 개선됨)

관련 참고자료:


4. XXE / SSRF (파서 설정 오류)

일부 YAML 파서는 내부적으로 XML을 사용하거나, 외부 엔티티·리소스 로딩을 지원하는 경우가 있다.

공격 시나리오

# 특정 파서에서 외부 파일 참조 가능
data: !include file:///etc/passwd

# SSRF — 내부 네트워크 요청 유발
config: !include http://169.254.169.254/latest/meta-data/

대응책:

  • 외부 리소스 로딩 기능 비활성화
  • 파서의 safe 모드 사용

관련 CVE 및 참고자료:


5. 파서 구현체별 동작 불일치 (Parser Differential Attack)

YAML 스펙이 복잡하여 각 언어의 파서마다 동일한 입력에 다른 결과를 반환할 수 있다. 이를 이용해 앞단 검증을 우회할 수 있다.

공격 시나리오

[입력 YAML] → [검증 파서 A: 무해한 값으로 해석] → 통과
                     ↓
              [실행 파서 B: 악성 값으로 해석] → 피해 발생

실제 사례: WAF나 보안 검증 레이어가 Ruby YAML로 파싱하고, 실제 앱은 Python YAML로 파싱할 때 결과 불일치 발생.

대응책:

  • 파이프라인 내 모든 단계에서 동일한 파서 사용
  • 검증과 실행에 서로 다른 파서 사용 금지

관련 참고자료:


6. 템플릿 인젝션과의 결합

YAML은 Jinja2, Go 템플릿 등의 템플릿 언어와 자주 혼용된다. 이 경우 YAML 파싱 전에 템플릿이 먼저 평가되므로, 템플릿 인젝션과 결합된 공격이 가능하다.

공격 예시 (Ansible/Helm)

# 사용자 입력값이 템플릿에 삽입되는 경우
name: { { user_input } }

# 공격자 입력: {{ ''.__class__.__mro__[2].__subclasses__() }}

대응책:

  • 사용자 입력을 템플릿 변수에 직접 삽입 금지
  • 입력값 이스케이프 처리
  • 템플릿 샌드박스 모드 사용

관련 CVE 및 참고자료:

  • CVE-2021-38305 — Yamale YAML 스키마 검증기 코드 인젝션 (JFrog 분석). eval() 잘못 사용으로 스키마 파일을 통해 임의 Python 코드 실행 가능
  • Finding YAML Deserialization with Snyk Code — geokit-rails 등 실제 오픈소스 프로젝트의 YAML 인젝션 사례

취약점 요약표

취약점심각도영향주요 대응책
역직렬화 (RCE)🔴 Critical원격 코드 실행safe_load() 사용
앨리어스 폭탄🟠 High서비스 중단 (DoS)앨리어스 깊이 제한
타입 강제 변환🟡 Medium로직 우회, 인증 오류명시적 따옴표 사용
XXE / SSRF🟠 High내부 정보 유출외부 로딩 비활성화
파서 불일치🟡 Medium검증 우회단일 파서 통일
템플릿 인젝션🔴 Critical코드 실행, 데이터 탈취입력값 이스케이프

근본 원인

YAML의 보안 문제는 기능 과잉(feature bloat)에서 비롯된다:

  1. 암묵적 타입 변환 — 개발자의 의도와 다르게 동작
  2. Turing-complete에 가까운 복잡성 — 앵커/앨리어스, 태그, 다양한 스칼라 타입
  3. 파서 구현 난이도 — 스펙이 복잡해 파서마다 동작이 달라짐
  4. 기본 설정의 위험성 — 안전한 옵션이 기본이 아닌 경우가 많음

대안 검토

용도권장 대안
설정 파일TOML — 명시적 타입, 단순한 스펙
데이터 교환JSON — 타입 강제 변환 없음
복잡한 설정HCL (Terraform), Dhall — 타입 안전성
간단한 키-값.env / INI

작성일: 2026-03-16 with Sonnet 4.6


Backlinks