XML 인젝션은 애플리케이션이 데이터를 구문 분석하거나 해석하는 방식을 변경하기 위해 XML 입력을 조작하는 것을 말합니다. 사용자가 제어하는 입력이 적절한 유효성 검사 없이 XML 문서에 삽입될 때 발생하며, 공격자는 제어 흐름을 수정하거나 로직을 우회하거나 위험한 파서 동작을 트리거하는 노드, 속성, 엔티티 또는 페이로드를 삽입할 수 있습니다. 오늘날의 API가 많고 통합 중심의 생태계에서 XML 인젝션은 보안팀이 무시할 수 없는 현실적인 위협으로 남아 있습니다.
단순한 입력 변조와 달리 XML 인젝션은 XML 자체의 표현력을 악용하여 SOAP, SAML, IoT 디바이스, 기업 통합, 레거시 금융 시스템 등 복잡한 시스템에 영향을 미칩니다.
XML 주입이 여전히 중요한 이유
JSON이 최신 애플리케이션을 지배하고 있지만 XML은 엔터프라이즈 소프트웨어, 인증 프로토콜, 백엔드 통합에 깊숙이 내장되어 있습니다. 공격자는 XML 구조를 악용할 수 있습니다:
- 비즈니스 로직 변경
- 승인되지 않은 노드 삽입
- XPath 쿼리 조작
- XXE 스타일 파일 공개 트리거
- 스키마 유효성 검사 중단
- XML 기반 서비스 거부 원인
XML의 유연성 덕분에 오용이 특히 강력합니다.
실제 CVE 예제: CVE-2025-13526
CVE-2025-13526은 단순한 XML 구문 분석 오류로 인해 전체 파일이 노출되는 방법을 보여줍니다. 시스템이 XML 구성 파일의 업로드를 허용했지만 외부 엔티티 확인을 비활성화하지 못했습니다.
악성 페이로드 예시:
xml
<!DOCTYPE 데이터 [!
]>
&xxe;이름>
서버가 다음 내용을 반환했습니다. /etc/passwd를 통해 XML 인젝션과 XXE를 결합하여 중요한 파일을 노출하는 방법을 보여줍니다.
XML 인젝션의 공격 표면
공격자의 악용:
- 노드 주입
- 속성 주입
- XPath 쿼리 조작
- 스키마(XSD) 조작
- XXE 페이로드
- DoS를 위한 엔티티 확장
- XML 기반 워크플로우 내부의 로직 바이패스
각 카테고리는 시스템의 다른 계층에 영향을 미칩니다.
공격 예시(동일한 수량 보존)
- 노드 주입 조작
xml
ProductA아이템>주문>
페이로드가 주입되었습니다:
pgsql
true
그 결과 XML이 구조적으로 손상되어 권한이 없는 권한이 부여될 수 있습니다.
XPath 주입
python
쿼리 = f"//users/user[username/text()='{user}']"
악의적인 입력:
bash
' 또는 '1'='1
이렇게 하면 승인되지 않은 사용자 기록이 노출됩니다.
XXE 페이로드
xml
<!DOCTYPE foo [
]>&payload;
스키마 조작
xml
<xs:element name="amount" type="xs:string"/>
이렇게 하면 예상되는 유효성 검사 동작이 비활성화됩니다.
억 개의 웃음 DoS
xml
<!ENTITY a "ha"><!ENTITY b "&a;&a;"><!ENTITY c "&b;&b;">
대규모 확장은 구문 분석기에 과부하를 줍니다.
방어 기술
엔티티 해상도 비활성화
python
parser = etree.XMLParser(resolve_entities=False, no_network=True)
안전한 라이브러리 사용
python
defusedxml.ElementTree를 ET로 가져오기
강력한 스키마 유효성 검사
python
schema.validate(input_xml)
XML 문자열을 연결하지 마십시오.
더 안전한 접근 방식:
python
el = 엘리먼트("사용자") name_tag.text = 사용자_입력
비교 표
| 유형 | Target | 심각도 | 영향 |
|---|---|---|---|
| XML 주입 | 구조 조작 | 중간-높음 | 로직 바이패스 |
| XPath 주입 | 쿼리 제어 | 높음 | 무단 액세스 |
| XXE | 구문 분석기 오용 | 높음 | 파일 읽기 / SSRF |
| 스키마 주입 | 유효성 검사 바이패스 | Medium | 무결성 위험 |

자동화된 모의 침투 테스트의 XML 주입
최신 AI 기반 모의 침투 테스트 플랫폼(예: Penligent)은 구조 변이, XML 퍼징, XPath 페이로드 테스트, 파서 오설정 탐지를 수행합니다. 이러한 플랫폼
- XML 기반 공격 표면 발견
- 주입 지점 식별
- 스키마 동작 자동 학습
- 적응형 페이로드 생성
- 여러 구성에서 구문 분석기 동작 검증하기
이렇게 하면 탐지 범위가 크게 확장됩니다.
보안 기준선 및 자동화된 수정 권장 사항
자동화된 플랫폼도 제공할 수 있습니다:
- 구문 분석기 구성 감사
- 안전하지 않은 XML 라이브러리 탐지
- 엔티티 해상도 설정 확인
- 엄격한 XSD 유효성 검사 시행
- 안전하지 않은 XML 작업의 CI/CD 차단
이를 통해 탐지를 실행 가능한 문제 해결로 전환할 수 있습니다.
예제 스크립트
Semgrep 규칙 - Python XML 주입 / XXE / 안전하지 않은 구문 분석
파일을 만듭니다:
semgrep-rules/python-xml-injection.yaml
yaml
규칙:
규칙 1: xml.etree의 안전하지 않은 사용 - 기본 구문 분석기는 XXE에 취약합니다.
- ID: 파이썬-안전하지 않은 XML-트리 심각도: 에러 메시지: | xml.etree.ElementTree는 신뢰할 수 없는 XML에 대해 안전하지 않습니다. 기본적으로 외부 엔티티 확장을 비활성화하지 않습니다. 대신 defusedxml을 사용하세요. 메타데이터: cwe: "CWE-611" owasp: "A04:2021 XML 외부 엔티티" 패턴:
- 패턴을 사용합니다: | xml.etree.ElementTree를 ET 언어로 가져옵니다: [파이썬]
규칙 2: xml.dom.minidom 직접 사용 - 안전하지 않은 XML 파서
- ID: python-unsafe-xml-minidom 심각도: 에러 메시지: | xml.dom.minidom은 DTD 또는 엔티티 확장을 비활성화하지 않습니다. 신뢰할 수 없는 XML에 사용하지 마세요. 메타데이터: cwe: "CWE-611" 패턴: | xml.dom.minidom 언어 가져오기: [파이썬]
규칙 3: resolve_entities = True인 위험한 lxml XMLParser
- ID: python-lxml-resolve-entities-enabled severity: 에러 메시지: | resolve_entities=True로 lxml XMLParser()를 사용하면 XXE가 활성화됩니다. resolve_entities=False 및 load_dtd=False로 설정합니다. 메타데이터: cwe: "CWE-611" 패턴: | XMLParser(resolve_entities=True, ...) 언어: [python]
규칙 4: load_dtd=True인 lxml - 위험함
- ID: python-lxml-load-dtd 심각도: 경고 메시지: | load_dtd=true는 DTD 처리를 활성화하고 엔티티 확장을 허용할 수 있습니다. 꼭 필요한 경우가 아니면 비활성화하세요. 메타데이터: cwe: "CWE-611" 패턴: | XMLParser(load_dtd=True, ...) 언어: [python]
규칙 5: lxml.fromstring() 사용 시 안전 구문 분석기 구성 누락
- id: python-lxml-fromstring-no-safe-config 심각도: 경고 메시지: | 강화된 XMLParser 없이 lxml.fromstring()을 호출했습니다. resolve_entities=False, no_network=True인지 확인합니다. 메타데이터: cwe: "CWE-611" 패턴:
- 패턴: | lxml에서 etree 가져오기 - 패턴: | etree.fromstring($XML) 언어: [python]
규칙 6: 외부 XML을 구문 분석할 때 defusedxml을 사용하지 않기
- ID: 파이썬-정의되지 않은 XML-사용되지 않은 심각도: INFO 메시지: | 파이썬에서 보안 XML 구문 분석에 권장되는 defusedxml 사용이 누락되었습니다. 메타데이터: cwe: "CWE-611" 패턴:
- 패턴: | ET.parse($X) - 패턴: | etree.parse($X) 언어: [python]
규칙 7: 강제 디퓨즈된 구문 분석기를 사용하지 않는 XMLTODICT
- ID: python-xmltodict-안전하지 않은 심각도: 경고 메시지: | xmltodict는 XXE를 허용하는 기본 XML 구문 분석기를 호출할 수 있습니다. 사용
defusedxml파서를 대신 사용합니다: | xmltodict.parse($X) 언어: [파이썬]
규칙 8: XML 파생 입력에서 eval()의 위험한 사용
- ID: python-eval-from-XML 심각도: CRITICAL 메시지: | XML 파생 입력에 대한 eval()은 RCE로 이어질 수 있습니다. 구문 분석된 XML 값을 직접 평가하지 마세요. 메타데이터: cwe: "CWE-95" owasp: "A03:2021 인젝션" 패턴:
- 패턴: | $VAL = $XML.xpath(...) - 패턴: | eval($VAL) 언어: [python]
규칙 9: XML 소스 데이터의 안전하지 않은 pickle.loads
- ID: 파이썬-피클-온-XML 심각도: 치명적 메시지: | XML에서 파생된 입력에 대한 pickle.loads()는 임의의 코드 실행을 초래할 수 있습니다. 사용자 데이터에 대한 피클을 피하세요. 메타데이터: cwe: "CWE-502" 패턴:
- 패턴: | $DATA = etree.fromstring(...) - 패턴: | pickle.loads($DATA) 언어: [python]`
파이썬 SAST 스캐너 완성
이는 다음과 같은 위험 패턴을 스캔하여 Semgrep을 보완합니다:
- 안전하지 않은 XML 파서 가져오기
- 엔티티/도큐타입 사용
평가,exec,피클,마샬,yaml.load패턴- XXE 트리거
.xml,.xsd,.wsdl파일
만들기: python_sast_scanner.py
python
#!/usr/bin/env python3""" python_sast_scanner.py 가볍고 빠른 파이썬 전용 SAST 스캐너입니다:
- XML 인젝션 / XXE
- 안전하지 않은 파서 사용
- 위험한 함수(eval, exec, pickle.loads, yaml.load) CI에 안전하며 코드를 실행하지 않습니다. 결과에 대해 JSON과 0이 아닌 출구를 출력합니다. """import os, re, json, sys PATTERNS = {# XML / XXE "xml_etree": r"import\s+xml\.etree\.ElementTree","xml_minidom": r"import\s+xml\.dom\.minidom","lxml_resolve_entities": r"resolve_entities\s*=\sTrue","lxml_load_dtd": r"load_dtd\s=\s*True","doctype_in_xml": r"<!DOCTYPE","entity_in_xml": r"<!ENTITY",

위험한 기능
"eval_usage": r"\\beval\\s*\\(","exec_usage": r"\\bexec\\s*\\(","pickle_loads": r"pickle\\.loads\\s*\\(","marshal_loads": r"marshal\\.loads\\s*\\(","yaml_unsafe_load": r"yaml\\.load\\s*\\(",
} IGNORED = {".git", "venv", "env", "pycache", "dist", "build", "node_modules"} def scan_file(path): findings = []try:with open(path, "r", encoding="utf-8″, errors="ignore") as f:for i, line in enumerate(f, 1):for rule, regex in PATTERNS.items():if re.search(regex, line): findings.append({"rule": rule,"line": line.strip(),"lineno": i, })except Exception:pass return findings def walk(root="."): results={}for dp, dirs, files in os.walk(root): dirs[:] = [d for d in dirs if d가 IGNORED에 없으면]for f in files:if f.endswith((".py",".xml",".xsd",".wsdl")): full = os.path.join(dp, f) hits = scan_file(full)if hits: results[full] = hitsreturn results def main(): root = sys.argv[1] if len(sys.argv)>1 else "." results = walk(root)print(json.dumps({"results": results, "file_count": len(results)}, indent=2))if results: sys.exit(3) sys.exit(0) if name == "main": main()`
다루는 내용 - 파이썬 SAST 범위
XML 인젝션 / XXE
안전하지 않은 XML 파서 ✔ lxml과 resolve_entities = True DTD 로딩(잠재적으로 위험할 수 있음) ✔ 저장된 XML 파일의 XXE 마커
코드 삽입
✔ eval() ✔ exec()
안전하지 않은 역직렬화
✔ pickle.loads() ✔ marshal.loads() 안전하지 않음 yaml.load()
기타 안전하지 않은 패턴
엔티티/도큐먼트의 엔티티/도큐먼트 유형 .xml, .xsd, .wsdl 강화된 구문 분석기가 없는 XMLTODICT
결론
XML 인젝션은 여전히 관련성이 높고 위험한 취약점 카테고리입니다. 비즈니스 로직 우회부터 파일 공개 및 서비스 거부에 이르기까지 다양한 영향을 미칩니다. XML 동작을 이해하고, 파서를 보호하고, 구조를 검증하고, 자동화된 침투 테스트를 통합하는 것은 강력한 애플리케이션 보안을 보장하는 데 필수적인 단계입니다.

