Skip to content

fix: PomParser의 XML 외부 엔티티(XXE) 취약점 수정 - #147

Merged
eGovFrameSupport merged 1 commit into
eGovFramework:mainfrom
EricSeokgon:fix/xxe-pomparser
Sep 7, 2026
Merged

fix: PomParser의 XML 외부 엔티티(XXE) 취약점 수정#147
eGovFrameSupport merged 1 commit into
eGovFramework:mainfrom
EricSeokgon:fix/xxe-pomparser

Conversation

@EricSeokgon

Copy link
Copy Markdown
Contributor

문제

PomParser.parse(File) 는 사용자 workspace 의 pom.xml 을 JDOM SAXBuilder 로 파싱하면서 외부 엔티티 처리를 제한하지 않습니다. 오염된 pom.xml 을 가진 프로젝트를 IDE 로 열면 XXE(CWE-611) 로 로컬 파일 읽기·SSRF 가 가능합니다.

#138 리뷰에서 @swanpark8538 님이 알려주신 동일 유형의 미방어 파서 중 하나이며, 요청하신 대로 개별 PR 로 분리했습니다.

변경

  • 외부 엔티티·외부 DTD 요청을 빈 스트림으로 처리하는 EntityResolver 설정
  • external-parameter-entities, nonvalidating/load-external-dtd 비활성화

JDOM 특이사항 (실측)

DOM 계열과 달리 SAXBuilder.setFeature("http://xml.org/sax/features/external-general-entities", false)효과가 없습니다. JDOM 1.1.1 의 SAXBuilder 가 파서를 구성할 때 이 feature 를 expandEntities 값(기본 true)으로 덮어쓰기 때문입니다. 그래서 EntityResolver 로 차단했습니다.

설정 정상 pom 내부 엔티티 pom XXE pom
현재 코드 0.0.1-SNAPSHOT 9.9.9-ENT secret-file-content (유출)
external-general-entities=false 만 적용 0.0.1-SNAPSHOT 9.9.9-ENT secret-file-content (차단 실패)
disallow-doctype-decl=true 0.0.1-SNAPSHOT 파싱 실패 파싱 실패
본 PR 0.0.1-SNAPSHOT 9.9.9-ENT 빈 값 (차단)

검증

egovframework.rte.rdt 의 pom 패키지를 lib/jdom.jar(1.1.1) 와 함께 컴파일하고, 변경 전/후 PomParser.parse() 를 실제 pom.xml 3종으로 실행해 비교했습니다.

  • 정상 pom(리포지터리 내 실제 파일): groupId·version·dependencies 3건 모두 기존과 동일
  • 내부 엔티티 pom(<!ENTITY egovVer "9.9.9-ENT">): version 이 9.9.9-ENT 로 기존과 동일하게 치환 — XmlUtil의 XML 외부 엔티티(XXE) 취약점 수정 #138 리뷰에서 지적해 주신 setExpandEntityReferences(false) 류의 부작용은 없습니다
  • XXE pom(<!ENTITY xxe SYSTEM "file:///...">): 기존 secret-file-content → 변경 후 빈 값

참고

원본 파일 끝에 개행이 없어 GitHub 웹 편집기가 저장하면서 EOF 개행 1줄을 자동으로 추가했습니다(diff 의 \ No newline at end of file 항목). 코드 변경은 아닙니다.

관련: #138

SAXBuilder가 외부 엔티티를 해석하지 않도록 EntityResolver와 파서 feature를 설정한다.
내부 엔티티 치환과 정상 pom.xml 파싱 결과는 기존과 동일하다.
@eGovFrameSupport
eGovFrameSupport merged commit 23b792b into eGovFramework:main Sep 7, 2026
1 check passed
swanpark8538 added a commit that referenced this pull request Sep 7, 2026
PR #147 은 PomParser 에만 외부 엔티티 차단을 인라인으로 넣었고, 같은
번들에서 같은 호출 경로(TableList)로 사용자 프로젝트의 services.xml 과
사용자가 입력한 경로의 파일을 읽는 ServicesParser 는 기본 SAXBuilder 로
남아 있어 오염된 services.xml 로 로컬 파일 읽기·SSRF(CWE-611) 가 여전히
가능했다.

방어 설정을 egovframework.rte.rdt.xml.SecureSAXBuilder 로 올리고 두
파서가 함께 쓰도록 한다. JDOM 1.1.1 의 SAXBuilder.setFeature 는 값을
저장만 하고 build() 시점에 파서가 인식하지 못하는 feature 를
JDOMException 으로 바꾸므로, Xerces 계열이 아닌 SAX 드라이버에서는
Apache 전용 feature 때문에 정상 문서까지 파싱에 실패했다. 이를 피해
configureParser 에서 XMLReader 에 직접 적용하면서 미인식 feature 는
건너뛴다. 외부 일반 엔티티는 JDOM 이 external-general-entities feature
를 expandEntities 값으로 덮어쓰므로 빈 스트림을 돌려주는 EntityResolver
로 차단하며, 내부 엔티티 치환은 기존과 동일하게 동작한다.

Refs: #147
@swanpark8538

Copy link
Copy Markdown
Collaborator

PR #138에 이어 추가 PR을 해 주셔서 감사합니다. 리뷰 과정에서 확인한 내용과 후속 조치를 공유드립니다.

PomParser를 확인하면서, 같은 번들의 ServicesParser(egovframework/rte/rdt/service/parser/ServicesParser.java)도 동일한 호출 경로(TableList)에서 사용자 프로젝트의 services.xml을 기본 SAXBuilder로 파싱하고 있어 같은 XXE 이슈가 남아 있었습니다.

또한, JDOM 1.1.1의 SAXBuilder.setFeature()는 값을 저장만 하고 build() 시점에 파서에 적용하는데, 이때 파서가 인식하지 못하는 feature는 JDOMException으로 바뀝니다. 그래서 Xerces 계열이 아닌 SAX 드라이버가 선택되면 nonvalidating/load-external-dtd 같은 Apache 전용 feature 때문에 정상 pom.xml까지 파싱에 실패할 수 있습니다. setFeature() 호출을 try/catch로 감싸는 것으로는 막을 수 없고, configureParser를 재정의해 XMLReader에 직접 적용하면서 미인식 feature를 건너뛰어야 했습니다.

두 가지를 함께 처리하기 위해 이번 PR의 방어 설정을 egovframework/rte/rdt/xml/SecureSAXBuilder.java 공통 클래스로 올리고 PomParserServicesParser가 이를 사용하도록 했습니다(55d3d65). 내부 엔티티 치환은 기존과 동일하게 유지되며, 테스트는 SecureSAXBuilderTest에 있습니다.

전자정부 표준프레임워크에 기여해 주셔서 감사합니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants