구현단계 보안약점 기준 - 부적절한 XML 외부개체 참조
유형 | 입력데이터 검증 및 표현 |
보안약점 | 부적절한 XML 외부개체 참조 |
개요 |
XML 문서에는 DTD(Document Type Definition)를 포함할 수 있으며, DTD는 XML 엔티티(entitiy)*를 정의한다.
부적절한 XML 외부개체 참조 보안약점은 서버에서 XML 외부 엔티티를 처리할 수 있도록 설정된 경우에 발생할 수 있다.
취약한 XML parser가 외부 값을 참조하는 XML 값을 처리할 때, 공격자가 삽입한 공격 구문이 동작되어 서버 파일 접근, 불필요한 자원 사용, 인증 우회, 정보 노출 등이 발생 할 수 있다.
* 반복적인 문장이나 문자열을 저장해놓고 쉽게 참조할 수 있도록 함
|
보안대책 |
로컬 정적 DTD를 사용하도록 설정하고, 외부에서 전송된 XML문서에 포함된 DTD를 완전하게 비활성화 해야 한다.
비활성화를 할 수 없는 경우에는 외부 엔티티 및 외부 문서 유형 선언을 각 파서에 맞는 고유한 방식으로 비활성화 한다.
|
진단방법 |
XML 파일을 파싱하고 있는지 확인한 후 신뢰할 수 없는 외부 입력값의 파일에 대한 외부 엔티티를 비활성화 하는 코드가 없을 경우 취약하다고 판정한다.
|
연관된 설계단계 기준 | XML 조회 및 결과 검증 |
코드예제
receivedXML
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]><foo>&xxe;</foo>
● 안전하지 않은 코드 예 (Java)
public void unmarshal(File receivedXml)
throws JAXBException, ParserConfigurationException, SAXException, IOException {
JAXBContext jaxbContext = JAXBContext.newInstance( Student.class );
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
// 입력받은 receivedXml 을 이용하여 Document를 생성한다.
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(receivedXml);
// 외부 엔티티로 만들어진 document를 이용하여 마샬링을 수행하여 안전하지 않다.
Student employee = (Student) jaxbUnmarshaller.unmarshal( document );
}
다음 예제는 class XXE에서 외부개체 참조 제한 설정 없이 secure.xml을 참조하고 있다.
이 때, secure.xml이 아래와 같을 때 /dev/tty 콘솔이 실행되어 입력 요청을 대기하는 서비스 거부 공격이 발생할 수 있다.
secure.xml
<?xml version="1.0"?>
<!DOCTYPE foo SYSTEM "file:/dev/tty">
<foo>bar</foo>
● 안전하지 않은 코드 예 (Java)
import javax.xml.parsers.SAXParsers;
import javax.xml.parsers.SAXParserFactory;
class XXE {
public static void main(String[] args)
throws FileNotFoundException, ParserConfigurationException, SAXException, IOException {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
// 외부개체 참조 제한 설정 없이 secure.xml 파일을 읽어서 파싱하여 안전하지 않다.
saxParser.parse(new FileInputStream("secure.xml"), new DefaultHandler());
}
}
● 안전한 코드 예 (Java)
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// XML 파서가 doctype을 정의하지 못하도록 설정한다.
dbf.setFeature("http://apache.org/xml/featuresdisallow-doctype-decl", true);
// 외부 일반 엔티티를 포함하지 않도록 설정한다.
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
// 외부 파라미터도 포함하지 않도록 설정한다.
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
// 외부 DTD 비활성화한다.
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
// XIncude를 사용하지 않는다.
dbf.setXIncludeAware(false);
// 생성된 파서가 엔티티 참조 노드를 확장하지 않도록 한다.
dbf.setExpandEntityReferences(false);
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(receivedXml);
Model model = (Model) u.unmarshal(document);
● 안전한 코드 예 (PHP)
value = libxml_disable_entity_loader(true);
$dom = new DOMDocument();
$dom -> loadXML($xml);
libxml_disable_entity_loader($value);
진단방법

● 일반적인 진단의 예
public class G06 extends HttpServlet {
private ServletFileUpload uploader = null;
...
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
...
try {
List<FileItem> fileItemsList = uploader.parseRequest(request);
Iterator<FileItem> fileItemsIterator = fileItemsList.iterator();
while (fileItemsIterator.hasNext()) {
FileItem fileItem = fileItemsIterator.next();
File xmlFile = new File( request.getServletContext().getAttribute("FILES_DIR") + File.separator+ fileItem.getName() );
fileItem.write(xmlFile);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(xmlFile); ·········································①
changeConfiguration(document);
...
}
● 정탐코드
@RequestMapping(value = "/xmlupload", method = RequestMethod.POST)
public Student xmlUpload(@RequestParam("file") MultipartFile multipartFile) {
File xmlFile = new File(multipartFile.getOriginalFilename());
multipartFile.transferTo(xmlFile);
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
SAXParseraxParser.parse(new FileInputStream(xmlFile), new DefaultHandler());
SAXParserFactory 사용하는 예제에서는 사용자 입력값인 XML file을 사용하고 있으나, 외부 엔티티 사용을 비활성화하지 않으므로 취약하다고 판정한다.
● 오탐코드
String xml = "xxe.xml";
DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
DocumentBuilder builder = df.newDocumentBuilder();
Document document = builder.parse(new InputSource(xml));
DOMSource domSource = new DOMSource(document);
DocumentBuilderFactory를 사용하는 예제에서는 3~4라인과 같은 제한 설정을 추가할 수 있다.
● 오탐코드
String xml = "xxe.xml";
DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
DocumentBuilder builder = df.newDocumentBuilder();
Document document = builder.parse(new InputSource(xml));
DOMSource domSource = new DOMSource(document);
DocumentBuilderFactory를 사용하는 예제에서는 3~4라인과 같은 제한 설정을 추가할 수 있다.
● 오탐코드
String xml = "xxe.xml";
SaxHandler handler = new SaxHandler();
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
parser.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
parser.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
parser.parse(xml, handler);
SAXParserFactory 예제의 경우 5~6라인과 같이 제한 설정을 추가할 수 있다.
● 오탐코드
SchemaFactory factory = SchemaFactory.newInstance("XMLConstants.W3C_XML_SCHEMA_NS_URI");
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
Schema schema = factory.newSchema(Source);
SchemaFactory의 경우 SchemaFactory 또는 Validator 클래스의 setProperty()를 사용하여 제한 설정을 추가할 수 있다.
● 오탐코드
SchemaFactory factory = SchemaFactory.newInstance("XMLConstants.W3C_XML_SCHEMA_NS_URI");
Schema schema = factory.newSchema();
Validator validator = schema.newValidator();
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
SchemaFactory의 경우 SchemaFactory 또는 Validator 클래스의 setProperty()를 사용하여 제한 설정을 추가할 수 있다.
● 오탐코드
XMLInputFactory factory = XMLInputFactory.newFactory();
factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
XMLEventReadereventReader= factory.createXMLEventReader(new FileReader("xxe.xml"));
XMLInputFactory 클래스의 setProperty()를 사용하여 제한 설정을 추가할 수 있다.