728x90
728x90
# [Java XML] DocumentBuilderFactory를 이용한 안전한 XML 파싱과 XXE Injection 방지하기
안녕하세요! 오늘은 Java에서 XML을 안전하게 파싱하고 XXE(XML External Entity) Injection 공격을 방지하는 방법에 대해 알아보겠습니다.
목차
- DocumentBuilderFactory란?
- XML 파싱 기본 방법
- XXE Injection이란?
- 안전한 XML 파싱 구현하기
- 실전 예제 코드
1. DocumentBuilderFactory란?
DocumentBuilderFactory는 Java에서 제공하는 XML 파싱을 위한 기본 클래스입니다. XML 문서를 파싱하여 DOM(Document Object Model) 객체로 변환하는 역할을 합니다.
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.parse(inputStream);
2. XML 파싱 기본 방법
2.1 XML 속성 추출하기
// XML 예시
<user id="123">
<name>John Doe</name>
<email>john@example.com</email>
</user>
// Java 코드
Element element = (Element) node;
String userId = element.getAttribute("id"); // "123" 반환
2.2 XML 요소 값 추출하기
// 텍스트 컨텐츠 가져오기
String name = element.getElementsByTagName("name")
.item(0)
.getTextContent(); // "John Doe" 반환
// NodeList를 이용한 반복 처리
NodeList nodeList = doc.getElementsByTagName("user");
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
// 처리 로직
}
}
3. XXE Injection이란?
XXE Injection은 XML 외부 개체를 이용한 보안 취약점 공격 방식입니다. 공격자가 XML 입력을 통해 서버의 파일을 읽거나, 서비스 거부 공격을 실행할 수 있습니다.
3.1 XXE 공격 예시
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>
<data>&xxe;</data>
</root>
이러한 공격은 서버의 중요 파일을 노출시킬 수 있으며, 심각한 보안 위험을 초래할 수 있습니다.
4. 안전한 XML 파싱 구현하기
4.1 보안 설정
public class SecureXMLParser {
private DocumentBuilderFactory createSecureDocumentBuilderFactory() {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
// XXE 방지를 위한 보안 설정
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
dbf.setFeature("http://apache.org/xml/features/disallow-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);
dbf.setExpandEntityReferences(false);
dbf.setXIncludeAware(false);
dbf.setValidating(false);
return dbf;
} catch (ParserConfigurationException e) {
throw new RuntimeException("XML 파서 구성 오류", e);
}
}
}
4.2 주요 보안 설정 설명
설정 | 설명 |
---|---|
FEATURE_SECURE_PROCESSING | 안전한 XML 처리 활성화 |
disallow-doctype-decl | DTD 사용 비활성화 |
external-general-entities | 외부 일반 엔티티 비활성화 |
external-parameter-entities | 외부 파라미터 엔티티 비활성화 |
setExpandEntityReferences | 엔티티 참조 확장 비활성화 |
5. 실전 예제 코드
5.1 완성된 보안 XML 파서
public class SecureXMLParser {
private static final Logger logger = LogManager.getLogger(SecureXMLParser.class);
public Document parseXML(InputStream input) {
try {
DocumentBuilderFactory dbf = createSecureDocumentBuilderFactory();
DocumentBuilder builder = dbf.newDocumentBuilder();
// 에러 핸들러 설정
builder.setErrorHandler(new ErrorHandler() {
@Override
public void warning(SAXParseException e) {
logger.warn("XML 파싱 경고: " + e.getMessage());
}
@Override
public void error(SAXParseException e) throws SAXException {
throw e;
}
@Override
public void fatalError(SAXParseException e) throws SAXException {
throw e;
}
});
return builder.parse(input);
} catch (Exception e) {
logger.error("XML 파싱 중 오류 발생", e);
throw new RuntimeException("XML 파싱 실패", e);
}
}
// XML 데이터 추출 예제
public UserData extractUserData(Document doc) {
UserData userData = new UserData();
NodeList userNodes = doc.getElementsByTagName("user");
for (int i = 0; i < userNodes.getLength(); i++) {
Node node = userNodes.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
userData.setId(element.getAttribute("id"));
userData.setName(getElementText(element, "name"));
userData.setEmail(getElementText(element, "email"));
}
}
return userData;
}
private String getElementText(Element parent, String tagName) {
NodeList nodeList = parent.getElementsByTagName(tagName);
if (nodeList.getLength() > 0) {
return nodeList.item(0).getTextContent();
}
return null;
}
}
5.2 사용 예제
public class XMLParserExample {
public static void main(String[] args) {
SecureXMLParser parser = new SecureXMLParser();
try (InputStream is = new FileInputStream("user_data.xml")) {
Document doc = parser.parseXML(is);
UserData userData = parser.extractUserData(doc);
System.out.println("사용자 ID: " + userData.getId());
System.out.println("이름: " + userData.getName());
System.out.println("이메일: " + userData.getEmail());
} catch (Exception e) {
e.printStackTrace();
}
}
}
보안 체크리스트
- DTD 비활성화
- 외부 엔티티 비활성화
- 보안 프로세싱 활성화
- 에러 핸들링 구현
- 로깅 구현
- 입력 검증
마치며
XML 파싱은 매우 유용하지만, 보안에 신경 쓰지 않으면 심각한 취약점이 될 수 있습니다. 이 글에서 설명한 보안 설정들을 반드시 적용하고, 정기적인 보안 검토를 통해 새로운 취약점에 대비해야 합니다.
참고 자료
본 글이 XML 파싱과 보안에 대한 이해를 높이는 데 도움이 되었기를 바랍니다. 궁금한 점이나 추가적인 의견이 있으시다면 댓글로 남겨주세요! 😊
#Java #XML #보안 #XXE #DocumentBuilderFactory #웹보안
728x90
300x250
'Java' 카테고리의 다른 글
[Java; 자바] IntelliJ IDEA에서 Java 버전 변경하는 완벽 가이드 (New) (0) | 2024.10.31 |
---|---|
[Java; 자바] 빌드 도구 면접 완벽 대비 블로그 (0) | 2024.10.24 |
[Java; 자바] Java 오버로딩과 오버라이딩: 개념, 차이점, 그리고 스프링부트 활용 예제 (0) | 2024.09.23 |
[Java; 자바] StringUtils / isBlank / isEmpty / org.apache.commons.lang3 / 공백 체크 / null 체크 (0) | 2024.01.04 |
[Java; 자바] 초보자를 위한 자바 프로그래밍 기초 안내 - 03 (0) | 2023.12.05 |