Popatrzmy na przykładowy kod w Javie
.
import java.io.File;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
public class Main {
public static void main(String[] args) {
try {
File xmlFile = new File("c:\\od0dopentestera\\3\\3.xml");
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(xmlFile);
System.out.println(doc.getDocumentElement().getTextContent());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Pobiera on treść pliku 3.xml
a następnie przy pomocy DocumentBuilder
1 przetwarza jego treść.
W ostatniej linijce pobieramy element główny tego dokumentu i wyświetlamy jego treść na ekranie.
Jak wygląda przykładowy plik wejściowy? Rozpoczyna się od deklaracji xml
a następnie atrybutu wersji, który jest tutaj wymagany.
Jeśli nie podamy wersji - plik nie może zostać przetworzony, dostaniemy komunikat o błędzie.
Następnie zdefiniowany zostaje element główny test
z przykładową wartością: demo
.
Jeśli więc nasz przykładowy kod działa prawidłowo, na ekranie powinniśmy zobaczyć ciąg: demo
.
Standard XML jest jednak dużo bardziej rozbudowany2.
Jedną z dodatkowych funkcjonalności jest możliwość tworzenia encji.
Przypominają one makra na przykład z języka C.
Krótko mówiąc możemy zdefiniować jakiś szablon z długą treścią.
Zamiast w każdym dokumencie wklejać tą samą treść - wystarczy użyć encji.
Parser automatycznie znajdzie wszystkie encje i zamieni ich treść na ten długi ciąg danych, wcześniej przez nas zdefiniowany.
Zobaczmy przykład.
Encje tworzy się przy pomocy słowa kluczowego DOCTYPE
oraz ENTITY
.
W moim przypadku nowo stworzona encja nazywa się zamień
.
Jej wartość natomiast to: bardzo długi tekst
.
Teraz w elemencie głównym odnoszę się do encji używając: &zamień;
.
Po uruchomieniu parsera widzimy, że encja zamień
została zastąpiona naszym wcześniej zdefiniowanym tekstem.
Gdzie zatem jest ukryta dzisiejsza podatność?
Twórcy formatu stwierdzili, że fajnie by było, gdyby encje mogły być definiowane w zewnętrznych plikach.
Dzięki temu długie ciągi tekstu nie będą przeszkadzały w czytaniu plików xml.
Ta funkcjonalność została umożliwiona dzięki słowu kluczowemu SYSTEM
.
Następnie podajemy nazwę pliku, który zawiera treść encji.
I tutaj leży sedno całego problemu. Jak możesz się bowiem spodziewać atakujący może podać tutaj dowolną ścieżkę do pliku.
W moim przypadku istnieje plik sekret
, z tajną zawartością. Tworze więc encje XXE, która odczyta zawartość właśnie tego pliku.
Teraz gdy patrzymy na wynik - widzimy treść tego tajnego pliku, pochodzącego z serwera na którym uruchomiona jest dana aplikacja.
Atak ten nazywa się XXE - czyli XML External Entity.
Jak się zatem przed nim zabezpieczyć?
Jednym z rozwiązań może być uruchomienie specjalnego trybu: Secure processing
3, który standardowo jest wyłączony.
dbFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
Teraz podczas próby ataku dostaniemy informację o błędzie.
Jak widać przetwarzania plików XML może być skomplikowane.
Może właśnie dlatego coraz większą popularność zdobywa format json
?