XmlUtil의 XML 외부 엔티티(XXE) 취약점 수정 - #138
Conversation
DocumentBuilderFactory에 외부 엔티티 차단 설정을 추가해 CWE-611(XXE)을 방지한다. 정적 팩토리 2개와 지역 인스턴스 3개에 applyXxeProtection 헬퍼를 적용했으며, 저장소 자체 방어 패턴(ComResourceUtils:213)과 동형이다. 검증 파서는 DTD 로딩을 유지하도록 외부 엔티티만 차단한다.
|
XmlUtil.java 파일에서 DocumentBuilderFactory를 만들 때 외부 엔티티 차단 설정이 없어서 egovframe-plugins 내부에 있는 pom.xml은 센터 검증을 거친 것이므로 XXE 위험은 없을 것입니다. 취약점이 실제 공격으로 이어질 가능성은 낮지만 위험 가능성이 존재하므로 해당 취약점에 대한 보안 조치를 취하는 것에 동의합니다. 또한 PR 내용을 검증한 결과 기능상 이상이 없다는 것과 XXE 방어에 성공하는 것을 확인했습니다. 다만 72행에 일반적인 경우 pom.xml의 version에 치환값을 넣는 설정은 하지 않으므로 문제가 없을 것입니다. 문제가 발생할 수 있는 경우는 아래와 같은 케이스를 상정할 수 있습니다. 만약 사용자 프로젝트에 다음과 같은 pom.xml이 있다고 가정한다면 <!DOCTYPE project [ <!ENTITY egovVer "4.3.0"> ]>
<project>
<dependencies>
<dependency>
<artifactId>egovframework-rte-ptl-mvc</artifactId>
<version>&egovVer;</version>
</dependency>
</dependencies>
</project>
72행에 그러면 NullPointerException이 발생해 오류가 발생할 것입니다. 위와 같은 케이스로 pom.xml을 작성할 가능성은 실무에서는 거의 없을 것이지만, 전자정부 표준프레임워크에 기여해 주셔서 감사합니다. |
setExpandEntityReferences 는 외부 리소스 접근이 아니라 내부 엔티티 참조의 치환 여부를 결정하는 설정이라 XXE 방어와 무관하며, 내부 엔티티를 쓴 pom.xml 에서 값이 null 로 인식돼 오류를 유발할 수 있다는 리뷰를 수용해 제거한다. 외부 일반·파라미터 엔티티 차단, XInclude 비활성화, 비검증 파서의 외부 DTD 차단·보안 처리는 그대로 유지한다.
|
@swanpark8538 상세한 검토와 재현 케이스까지 짚어주셔서 감사합니다. 지적하신 내용에 동의하며 요청대로 반영했습니다.
검증 결과(기능 이상 없음·XXE 방어 성공)까지 확인해 주셔서 감사합니다. 다시 검토 부탁드립니다. |
|
PR에 합치겠다고 말씀하신 추가 작업들은 각각 개별로 PR 해주시기 바랍니다. 같은 XXE 위협에 노출된 미방어 파서
|
|
@swanpark8538 님, 리뷰와 병합 감사합니다. 알려주신 미방어 파서 4건을 각각 개별 PR로 올렸습니다.
각 PR은 변경 전/후를 실제로 실행해 비교했고, 특히 지적해 주셨던 내부 엔티티( 한 가지 공유드릴 실측 결과가 있습니다. builder.setFeature("http://xml.org/sax/features/external-general-entities", false);가 효과가 없습니다. SAXBuilder가 파서를 구성할 때 이 feature를 검토 부탁드립니다. |
수정 사유 Reason for modification
수정된 소스 내용 Modified source
XmlUtil이 XML을 파싱할 때DocumentBuilderFactory에 외부 엔티티 차단 설정을 하지 않아 XXE(XML External Entity Injection, CWE-611) 에 노출됩니다. 미방어 지점은 정적 팩토리 2개(45·46행)와 지역 인스턴스 3개(186·204·227행, 수정 전 기준)입니다.XmlUtil은 이 저장소의 6개 플러그인에서 호출되며, 그중HandlePomXMLFileUtil은 프로젝트의pom.xml을 파싱합니다. 따라서 외부 엔티티가 포함된 XML(예: 악의적pom.xml)이 든 프로젝트를 IDE로 열면 개발자 PC의 로컬 파일이 읽히거나 외부 URL 요청이 발생할 수 있습니다.수정 내용
이 저장소는 이미 같은 방어 패턴을 갖고 있습니다 —
egovframework.dev.imp.commngt의ComResourceUtils.java213행이setFeature계열로 외부 엔티티를 차단합니다. 이 PR은 그 패턴을XmlUtil에 맞춰 넣는 것으로 새 규칙을 도입하지 않습니다.applyXxeProtection(DocumentBuilderFactory, boolean)private 헬퍼를 추가하고 5개 파싱 지점에 적용했습니다(+33/−0). 외부 일반/파라미터 엔티티를 차단해 검증 여부와 무관하게 XXE 파일 읽기를 막습니다. 스키마 검증용vfactory(validating=true)는 DTD 로딩이 필요하므로 외부 엔티티 차단만 적용하고, 비검증 파서에는 외부 DTD 로딩 차단과FEATURE_SECURE_PROCESSING까지 적용했습니다. 파서가 특정 기능을 미지원하면ParserConfigurationException을 무시하고 나머지 방어를 적용합니다.JUnit 테스트 JUnit tests
빌드: 번들된
lib/xalan.jar를 클래스패스로javac단독 컴파일 성공(JDK 21, 오류 0).재현·검증 (수정 전/후 동일 하네스, JDK 21.0.10 —
applyXxeProtection의 실제 로직을 복제해 실행). 공격 XML(<!DOCTYPE r [ <!ENTITY xxe SYSTEM "file://…"> ]>)로 로컬 파일 읽기를 시도:v=[TOP-SECRET-EGOV-2026]v=[]v=[TOP-SECRET-EGOV-2026]v=[]정상 XML 회귀 확인(파싱은 계속 동작해야 함):
v=[정상데이터](정상)v=[정상데이터](정상)수정 후 외부 엔티티는 빈 값으로 무력화되고, 정상 XML 파싱과 스키마 검증 경로는 그대로 동작합니다.
테스트 브라우저 Test Browser
테스트 스크린샷 또는 캡처 영상 Test screenshots or captured video
UI 변경이 없어 스크린샷은 해당하지 않습니다. 위 표의 실측값을 근거로 제시합니다.
참고 — 같은 저장소의 동일 유형 (이 PR 범위 밖): 근본 원인이 같은 XXE 미방어 지점이
dev/mdev ComResourceUtils(각 2곳, 같은 파일의 다른 지점은 이미 방어됨),NexusPropertyPage,QueryIdSearchJob에도 있습니다. 이 PR은 호출자가 가장 많은XmlUtil한 파일로 범위를 한정했으며, 나머지도 동일 방식으로 후속 정리할 수 있습니다. 필요하시면 이 PR에 합치겠습니다.병합된 보안 수정 선례 (참고)
같은 조직(eGovFramework)에서 제가 제출한 동일 성격의 보안 수정들이 최근 병합되었습니다 — egovframe-common-components #1134(뷰 이름 인젝션 Locale 우회), #1156(상위 경로
..재생성 차단), #1163(팝업 URL 탭 문자 우회 차단). 본 PR 은 그 연장선에서 개발환경 쪽 XML 파서의 외부 엔티티 해석을 표준 권고(외부 DTD·엔티티 비활성화)대로 차단하는 것입니다.