Xml External Entities in Django
How Xml External Entities Manifests in Django
Xml External Entities (XXE) vulnerabilities in Django applications typically arise when developers use XML parsing libraries without proper configuration, allowing attackers to read files, access internal services, or perform server-side request forgery through crafted XML payloads.
Django itself doesn't directly parse XML in its core request handling, but XXE vulnerabilities commonly appear in these Django-specific contexts:
- REST Framework XML Parsers - When using
rest_framework_xmlor similar packages that enable XML content negotiation - Configuration Files - Settings files that accept XML input for feature flags or dynamic configuration
- Third-party Integrations - Payment gateways, document processing services, or external APIs that exchange XML data
- Custom Admin Interfaces - Admin views that allow XML file uploads or processing
- Middleware Processing - Custom middleware that parses XML from request bodies or headers
The most dangerous Django-specific attack pattern involves uploading XML files through admin interfaces. Consider this vulnerable admin configuration:
from django.contrib import admin
from .models import Document
@admin.register(Document)
class DocumentAdmin(admin.ModelAdmin):
list_display = ['name', 'uploaded_at']
# Vulnerable: allows XML file uploads without validation
An attacker could upload a malicious XML file containing:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >
]>
<foo>&xxe;</foo>
When this XML is parsed by a vulnerable library, it could expose sensitive system files, internal network services, or even trigger SSRF attacks against internal Django services.
Django-Specific Detection
Detecting XXE vulnerabilities in Django applications requires examining both code patterns and runtime behavior. Here's how to identify potential XXE issues in your Django codebase:
Code Analysis Patterns
Search your Django project for these vulnerable patterns:
# Vulnerable: Using xml.etree.ElementTree without disabling entities
import xml.etree.ElementTree as ET
# Vulnerable: Using lxml without entity resolution disabled
from lxml import etree
# Vulnerable: Using xml.dom.minidom without proper configuration
import xml.dom.minidom as minidom
Look for these specific function calls in your views, serializers, or utilities:
ET.parse(file) # Vulnerable if file is user-controlled
ET.fromstring(xml_data) # Vulnerable if xml_data is untrusted
etree.parse(file) # Vulnerable without parser options
etree.fromstring(xml_data) # Same vulnerability
Content-Type Header Analysis
Check your Django REST Framework settings for XML parsers:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
'rest_framework_xml.parsers.XMLParser', # Potential XXE vector
'rest_framework.parsers.JSONParser',
],
'DEFAULT_RENDERER_CLASSES': [
'rest_framework_xml.renderers.XMLRenderer',
'rest_framework.renderers.JSONRenderer',
]
}
Automated Scanning with middleBrick
middleBrick's black-box scanning approach is particularly effective for detecting XXE vulnerabilities in Django applications. The scanner tests for XXE by:
- Sending crafted XML payloads to endpoints that accept XML content types
- Checking for XML parsing errors that might indicate vulnerable configurations
- Testing for entity expansion and external entity resolution
- Scanning OpenAPI specs for XML endpoints that might be vulnerable
Run middleBrick from your terminal:
middlebrick scan https://your-django-app.com/api/
The scanner will automatically detect XML endpoints and test them for XXE vulnerabilities, providing specific findings with severity levels and remediation guidance.
Django-Specific Remediation
Securing Django applications against XXE requires both secure coding practices and proper library configuration. Here are Django-specific remediation strategies:
Secure XML Parsing Configuration
For Python's standard library xml.etree.ElementTree:
import xml.etree.ElementTree as ET
from xml.parsers.expat import ParserCreate
# Secure parsing - disable entity resolution
def parse_xml_secure(xml_data):
parser = ET.XMLParser(resolve_entities=False)
return ET.fromstring(xml_data, parser=parser)
# Alternative using expat parser
def parse_xml_expat(xml_data):
parser = ParserCreate()
parser.StartNamespaceDeclHandler = None
parser.EndNamespaceDeclHandler = None
parser.DefaultHandlerExpand = None
return ET.fromstring(xml_data, parser=parser)
For lxml library (recommended for Django applications):
from lxml import etree
def parse_xml_lxml_secure(xml_data):
parser = etree.XMLParser(resolve_entities=False, load_dtd=False)
return etree.fromstring(xml_data, parser=parser)
# For parsing files
def parse_file_lxml_secure(file_path):
parser = etree.XMLParser(resolve_entities=False, load_dtd=False)
with open(file_path, 'rb') as f:
return etree.parse(f, parser=parser)
Django REST Framework Security
Configure your DRF settings to use secure parsers:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser', # Prefer JSON over XML
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser',
# Remove XMLParser or use custom secure implementation
],
}
Admin Interface Protection
Secure your Django admin file uploads:
from django.contrib import admin
from .models import Document
from django import forms
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
fields = '__all__'
def clean_file(self):
file = self.cleaned_data.get('file')
if file and file.name.endswith(('.xml', '.xsd', '.xsl')):
# Reject XML files or scan them
raise forms.ValidationError('XML file uploads are not permitted')
return file
@admin.register(Document)
class DocumentAdmin(admin.ModelAdmin):
form = DocumentForm
list_display = ['name', 'uploaded_at']
Middleware Security
Add XXE protection at the middleware level:
class XXESecurityMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Block XML content types for sensitive endpoints
if 'CONTENT_TYPE' in request.META:
content_type = request.META['CONTENT_TYPE']
if 'xml' in content_type.lower():
# Log and reject XML content for sensitive views
if request.path.startswith('/admin/'):
return HttpResponseForbidden('XML content not allowed')
return self.get_response(request)
Frequently Asked Questions
How can I test if my Django application is vulnerable to XXE?
middlebrick scan https://your-django-app.com. The scanner tests for XXE by sending crafted XML payloads to endpoints that accept XML content types. Additionally, manually check your code for XML parsing without entity resolution disabled, and review your DRF settings for XML parsers.