Skip to content

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

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

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

Conversation

@EricSeokgon

Copy link
Copy Markdown
Contributor

문제

QueryIdSearchJob 은 워크스페이스의 .xml 파일을 DocumentBuilderFactory 로 파싱하면서 외부 엔티티 처리를 제한하지 않습니다. 기존 EntityResolver 는 iBATIS.com DTD 만 빈 스트림으로 처리하고 그 외에는 null 을 반환하므로, 나머지 외부 참조는 파서 기본 동작대로 해석됩니다. 오염된 XML 이 워크스페이스에 있으면 쿼리 ID 검색 과정에서 XXE(CWE-611) 로 로컬 파일 읽기·SSRF 가 가능합니다.

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

변경

외부 엔티티·외부 DTD 로딩을 차단하는 설정 4줄을 추가했습니다. 기존 setValidating(false) 와 iBATIS DTD 용 EntityResolver 는 그대로 두었습니다.

검증 (실측)

리포지터리에 있는 실제 MyBatis 매퍼(EgovArticleScrap_SQL_cubrid.xml, DOCTYPE 이 http://mybatis.org/dtd/mybatis-3-mapper.dtd 를 가리킴)를 변경 전/후 동일한 코드 경로로 파싱해 비교했습니다.

루트 엘리먼트 select 개수 직렬화 결과 파싱 시간
변경 전 mapper 3 3,514자 235ms
변경 후 mapper 3 3,514자 (완전 동일) 10ms
  • 파싱 결과는 문자열까지 동일하며, 외부 DTD 를 더 이상 내려받지 않아 오프라인 환경에서도 지연이 없습니다.
  • XXE 픽스처(<!ENTITY xxe SYSTEM "file:///...">)에서는 변경 전 파일 내용이 그대로 노출되던 것이 변경 후 차단됩니다.
  • 내부 엔티티(<!ENTITY ver "4.3.0">) 치환은 변경 전후 동일하게 동작합니다 — XmlUtil의 XML 외부 엔티티(XXE) 취약점 수정 #138 리뷰에서 지적해 주신 setExpandEntityReferences(false) 류의 부작용은 없습니다.

관련: #138

질의 ID 검색 시 워크스페이스 XML을 파싱하는 DocumentBuilderFactory에 외부 엔티티·외부 DTD 차단 설정을 추가한다.
@eGovFrameSupport
eGovFrameSupport merged commit 28881f5 into eGovFramework:main Sep 7, 2026
1 check passed
@swanpark8538

Copy link
Copy Markdown
Collaborator

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

swanpark8538 added a commit that referenced this pull request Sep 8, 2026
#148 에서 외부 DTD 로드와 외부 일반·파라미터 엔티티를 끈 뒤로 파서가 외부
자원을 요청하지 않으므로, iBATIS DTD 요청에만 빈 스트림을 돌려주던
EntityResolver 는 DOCTYPE PUBLIC·SYSTEM 이나 내부 서브셋의 외부 엔티티
어느 경우에도 호출되지 않는다.

이 resolver 는 publicId 가 null 인 SYSTEM 전용 DOCTYPE 문서에서
arg0.contains 가 NullPointerException 을 내어 해당 파일이 쿼리 ID 검색에서
조용히 빠지던 잠복 결함도 가지고 있었다. 도달하지 않는 resolver 블록과 그것만
쓰던 import 5개를 제거한다.

Refs: #148
@swanpark8538

Copy link
Copy Markdown
Collaborator

이번 설정으로 파서가 외부 DTD 와 외부 엔티티를 요청하지 않게 되면서, 기존 iBATIS 용 EntityResolver 는 DOCTYPE PUBLIC·SYSTEM 이나 내부 서브셋의 외부 엔티티 어느 경우에도 더 이상 호출되지 않습니다. 그리고 그 resolver 는 publicId 가 null 인 <!DOCTYPE ... SYSTEM "..."> 문서에서 arg0.contains(...) 가 NullPointerException 을 내어 해당 파일이 검색에서 빠지던 결함을 갖고 있었는데, 이번 변경으로 그 경로도 함께 사라졌습니다. 도달하지 않는 코드가 되었으므로 resolver 블록과 관련 import 는 68abb94 에서 제거해 main 에 반영했습니다.

참고로 core XmlUtil 에 보안 설정이 적용된 DocumentBuilderFactory 를 돌려주는 newSecureDocumentBuilderFactory() 를 추가했습니다(cf1407b). dbio 번들은 core 에 의존하지 않아 QueryIdSearchJob 의 차단 설정 4줄은 인라인 그대로 두었습니다.

swanpark8538 added a commit that referenced this pull request Sep 8, 2026
…cumentBuilderFactory 로 교체

cf1407b 에서 DOM 파서의 XXE 방어 설정을 core 의
XmlUtil.newSecureDocumentBuilderFactory 로 통합했지만, dbio 는 core 를
Require-Bundle 하지 않아 QueryIdSearchJob 만 같은 네 줄을 인라인으로
유지하고 있었다. 방어 설정이 두 곳에 남아 있으면 이후 설정을 바꿀 때
한쪽을 빠뜨릴 수 있다.

dbio 의 MANIFEST 에 egovframework.dev.imp.core 를 추가하고 헬퍼 호출로
교체한다. dbio 가 요구하는 ide 는 core 를 재노출하지 않으므로 직접
의존이 필요하다. dbio.feature 의 requires 목록도 MANIFEST 와 같이 core
를 선언한다. 헬퍼는 기존 네 가지 설정에 더해 FEATURE_SECURE_PROCESSING
을 켜고, 파서가 지원하지 않는 feature 는 건너뛴다.

Refs: #148
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