Django Insecure Deserialization

Critical Risk Deserialization
djangopythondeserializationpickleyamlcode-execution

What it is

The Django application uses insecure deserialization methods like pickle.loads(), yaml.load(), or eval() with untrusted data, potentially leading to remote code execution. Django applications should use safe serialization formats and avoid deserializing untrusted data that can instantiate arbitrary objects or execute code.

# Vulnerable: Insecure deserialization in Django import pickle import yaml from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator from django.views import View # Dangerous: Pickle deserialization class SessionDataView(View): def post(self, request): session_data = request.POST.get('session_data', '') try: # Extremely dangerous: pickle.loads with user data decoded_data = pickle.loads(session_data.encode('latin1')) # Store in session request.session['user_data'] = decoded_data return JsonResponse({'status': 'success'}) except Exception as e: return JsonResponse({'error': str(e)}) # Dangerous: YAML deserialization def import_config(request): if request.method == 'POST' and 'config_file' in request.FILES: config_file = request.FILES['config_file'] config_content = config_file.read().decode('utf-8') try: # Dangerous: yaml.load can execute arbitrary code config_data = yaml.load(config_content) # Process configuration update_app_config(config_data) return JsonResponse({'status': 'config_imported'}) except Exception as e: return JsonResponse({'error': str(e)}) # Dangerous: eval() with user input def calculate_expression(request): expression = request.GET.get('expr', '') try: # Extremely dangerous: eval with user input result = eval(expression) return JsonResponse({'result': result}) except Exception as e: return JsonResponse({'error': str(e)}) # Dangerous: Custom deserialization class UserProfileView(View): def post(self, request): profile_data = request.POST.get('profile', '') # Dangerous: exec with user-controlled code user_code = f"profile = {profile_data}" local_vars = {} try: exec(user_code, {}, local_vars) profile = local_vars.get('profile', {}) # Save profile request.user.profile.data = profile request.user.profile.save() return JsonResponse({'status': 'profile_updated'}) except Exception as e: return JsonResponse({'error': str(e)})
# Secure: Safe deserialization in Django import json import yaml from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator from django.views import View from django.core import serializers from django.core.exceptions import ValidationError import ast # Safe: JSON deserialization class SessionDataView(View): def post(self, request): session_data = request.POST.get('session_data', '') try: # Safe: JSON deserialization decoded_data = json.loads(session_data) # Validate data structure validated_data = self.validate_session_data(decoded_data) # Store in session request.session['user_data'] = validated_data return JsonResponse({'status': 'success'}) except (json.JSONDecodeError, ValidationError) as e: return JsonResponse({'error': 'Invalid data format'}, status=400) def validate_session_data(self, data): # Validate data structure and types if not isinstance(data, dict): raise ValidationError('Data must be a dictionary') allowed_keys = {'preferences', 'settings', 'theme'} validated = {} for key, value in data.items(): if key in allowed_keys: if key == 'preferences' and isinstance(value, dict): validated[key] = {k: v for k, v in value.items() if isinstance(k, str) and isinstance(v, (str, int, bool))} elif key == 'settings' and isinstance(value, dict): validated[key] = value elif key == 'theme' and isinstance(value, str): if value in ['light', 'dark', 'auto']: validated[key] = value return validated # Safe: YAML deserialization def import_config(request): if request.method == 'POST' and 'config_file' in request.FILES: config_file = request.FILES['config_file'] # Validate file size if config_file.size > 1024 * 1024: # 1MB limit return JsonResponse({'error': 'File too large'}, status=400) config_content = config_file.read().decode('utf-8') try: # Safe: yaml.safe_load prevents code execution config_data = yaml.safe_load(config_content) # Validate configuration structure validated_config = validate_config_structure(config_data) # Process configuration update_app_config(validated_config) return JsonResponse({'status': 'config_imported'}) except yaml.YAMLError as e: return JsonResponse({'error': 'Invalid YAML format'}, status=400) except ValidationError as e: return JsonResponse({'error': str(e)}, status=400) def validate_config_structure(config): if not isinstance(config, dict): raise ValidationError('Configuration must be a dictionary') required_keys = {'database', 'cache', 'debug'} allowed_keys = required_keys.union({'logging', 'email', 'static'}) # Check required keys for key in required_keys: if key not in config: raise ValidationError(f'Missing required key: {key}') # Validate each section validated = {} for key, value in config.items(): if key not in allowed_keys: continue # Skip unknown keys if key == 'database' and isinstance(value, dict): validated[key] = validate_database_config(value) elif key == 'cache' and isinstance(value, dict): validated[key] = validate_cache_config(value) elif key == 'debug' and isinstance(value, bool): validated[key] = value # Add other validations as needed return validated # Safe: Mathematical expression evaluation def calculate_expression(request): expression = request.GET.get('expr', '') # Validate expression format if not expression or len(expression) > 100: return JsonResponse({'error': 'Invalid expression'}, status=400) # Only allow safe mathematical expressions if not re.match(r'^[0-9+\-*/().\s]+$', expression): return JsonResponse({'error': 'Expression contains invalid characters'}, status=400) try: # Safe: Use ast.literal_eval for literals only # For more complex math, use a safe expression parser result = safe_math_eval(expression) return JsonResponse({'result': result}) except (ValueError, SyntaxError) as e: return JsonResponse({'error': 'Invalid expression'}, status=400) def safe_math_eval(expression): # Simple safe math evaluation (extend as needed) allowed_nodes = (ast.Expression, ast.BinOp, ast.UnaryOp, ast.Num, ast.Constant, ast.Add, ast.Sub, ast.Mult, ast.Div, ast.USub, ast.UAdd) try: # Parse expression tree = ast.parse(expression, mode='eval') # Validate all nodes are safe for node in ast.walk(tree): if not isinstance(node, allowed_nodes): raise ValueError('Unsafe operation in expression') # Evaluate safely return eval(compile(tree, '', 'eval')) except Exception: raise ValueError('Invalid mathematical expression') # Safe: User profile handling class UserProfileView(View): def post(self, request): try: # Safe: JSON deserialization profile_data = json.loads(request.body) # Validate profile data validated_profile = self.validate_profile_data(profile_data) # Update profile using Django ORM profile, created = UserProfile.objects.get_or_create(user=request.user) # Safe attribute setting for key, value in validated_profile.items(): if hasattr(profile, key): setattr(profile, key, value) profile.save() return JsonResponse({'status': 'profile_updated'}) except (json.JSONDecodeError, ValidationError) as e: return JsonResponse({'error': 'Invalid profile data'}, status=400) def validate_profile_data(self, data): if not isinstance(data, dict): raise ValidationError('Profile data must be a dictionary') allowed_fields = { 'first_name': str, 'last_name': str, 'email': str, 'phone': str, 'birth_date': str, 'bio': str } validated = {} for field, expected_type in allowed_fields.items(): if field in data: value = data[field] if isinstance(value, expected_type): # Additional validation per field if field == 'email': from django.core.validators import validate_email validate_email(value) elif field in ['first_name', 'last_name', 'bio']: value = value[:100] # Limit length elif field == 'birth_date': from datetime import datetime datetime.strptime(value, '%Y-%m-%d') # Validate date format validated[field] = value return validated # Safe: Django serialization framework def export_model_data(request): from myapp.models import MyModel # Safe: Django's built-in serialization queryset = MyModel.objects.filter(user=request.user) serialized_data = serializers.serialize('json', queryset) return JsonResponse({'data': serialized_data}) def import_model_data(request): if request.method == 'POST': try: # Safe: Django deserialization with validation data = request.POST.get('data', '') for obj in serializers.deserialize('json', data): # Validate before saving if obj.object.user == request.user: obj.save() return JsonResponse({'status': 'imported'}) except Exception as e: return JsonResponse({'error': 'Import failed'}, status=400)

💡 Why This Fix Works

See fix suggestions for detailed explanation.

Why it happens

Django applications use Python's pickle module to serialize session data, cached objects, or application state, then deserialize this data on subsequent requests without verifying integrity, enabling code execution when session storage or caches are compromised. Django's default session backend django.contrib.sessions.backends.db stores session data in database, and custom session backends using pickle.dumps() to serialize session dictionaries: pickled = pickle.dumps(request.session._session_cache); Session.objects.create(session_key=key, session_data=pickled) creates exploitable session storage. Cache-based session backends like django.contrib.sessions.backends.cache serialize entire session objects with pickle, storing them in Redis, Memcached, or database caches where attackers with cache access inject malicious pickled payloads. Django's cache framework using pickle as default serialization: cache.set('user_data', complex_object) implicitly uses pickle.dumps() to serialize objects for storage, and cache.get('user_data') deserializes with pickle.loads(), executing code embedded in cached data. Session fixation combined with pickle deserialization: attackers set known session IDs, pre-populate session storage with malicious pickled data, then trick users into using those session IDs, triggering code execution when Django deserializes poisoned sessions. Custom session engines that serialize user-uploaded data into sessions: request.session['uploaded_data'] = user_provided_object uses pickle when session data exceeds JSON-serializable types, storing objects that execute code during deserialization. Cache poisoning attacks where attackers gain temporary write access to Redis, Memcached, or database cache tables and replace legitimate cached objects with malicious pickled payloads that execute when Django applications retrieve cached data.

Root causes

Using pickle.loads() on Session Data or Cached Objects

Django applications use Python's pickle module to serialize session data, cached objects, or application state, then deserialize this data on subsequent requests without verifying integrity, enabling code execution when session storage or caches are compromised. Django's default session backend django.contrib.sessions.backends.db stores session data in database, and custom session backends using pickle.dumps() to serialize session dictionaries: pickled = pickle.dumps(request.session._session_cache); Session.objects.create(session_key=key, session_data=pickled) creates exploitable session storage. Cache-based session backends like django.contrib.sessions.backends.cache serialize entire session objects with pickle, storing them in Redis, Memcached, or database caches where attackers with cache access inject malicious pickled payloads. Django's cache framework using pickle as default serialization: cache.set('user_data', complex_object) implicitly uses pickle.dumps() to serialize objects for storage, and cache.get('user_data') deserializes with pickle.loads(), executing code embedded in cached data. Session fixation combined with pickle deserialization: attackers set known session IDs, pre-populate session storage with malicious pickled data, then trick users into using those session IDs, triggering code execution when Django deserializes poisoned sessions. Custom session engines that serialize user-uploaded data into sessions: request.session['uploaded_data'] = user_provided_object uses pickle when session data exceeds JSON-serializable types, storing objects that execute code during deserialization. Cache poisoning attacks where attackers gain temporary write access to Redis, Memcached, or database cache tables and replace legitimate cached objects with malicious pickled payloads that execute when Django applications retrieve cached data.

Deserializing YAML with Unsafe Loaders

Django applications parse YAML configuration files, user uploads, or API responses using yaml.load() without Loader parameter or with unsafe loaders, enabling arbitrary Python object instantiation and code execution through YAML tags. Configuration management systems using yaml.load() to parse settings files: config = yaml.load(open('config.yml')) executes Python code embedded in YAML through !!python/object/apply tags that invoke arbitrary functions with attacker-controlled arguments. Django management commands accepting YAML input files for data import, migration, or bulk operations: yaml.load(uploaded_file.read()) deserializes user-uploaded YAML containing malicious object constructors. API endpoints consuming YAML request bodies without safe parsing: data = yaml.load(request.body) enables attackers to send YAML payloads with embedded code execution directives. YAML fixture files for Django testing or data seeding that accept user contributions: developers loading untrusted YAML fixtures with yaml.load() during database population execute embedded malicious code. Integration with external systems providing YAML responses: config_response = requests.get(external_api); config = yaml.load(config_response.text) trusts external YAML without validation, enabling compromised external services to deliver code execution payloads. The yaml.load() function without explicit Loader defaults to FullLoader in PyYAML 5.1+ which still permits some Python object construction, while older versions default to highly dangerous unsafe loading. Django applications using YAML for serialization thinking it's a safer alternative to pickle, not realizing yaml.load() shares similar code execution risks without proper loader configuration.

Processing User-Uploaded Serialized Files

Django file upload handlers accept and process user-uploaded files containing serialized data in pickle, YAML, JSON, or custom formats, deserializing file contents without format validation, signature verification, or malicious content detection. Django forms with FileField or ImageField accept uploads that developers deserialize directly: uploaded_file = request.FILES['data_file']; data = pickle.loads(uploaded_file.read()) processes user-controlled pickle files enabling code execution. Import/export functionality allowing users to upload backup files, configuration exports, or data migrations that applications deserialize: django.core.serializers.deserialize('pickle', uploaded_content) can execute code if pickle format is used. Model import views that accept serialized model data: users upload files claiming to be Django fixture dumps that applications deserialize with loaddata management command or custom deserialization code. Bulk data processing endpoints that accept CSV, JSON, or YAML uploads where developers use unsafe deserialization for complex data types: attempting to deserialize nested structures or custom objects using pickle or unsafe YAML. Media file processing that embeds metadata in uploaded files (images with EXIF, PDFs with XMP, documents with properties) where Django applications extract and deserialize metadata without validation. Django admin import/export features using third-party packages that deserialize uploaded files: packages like django-import-export with custom serialization formats that don't validate file contents before deserialization. Lack of file type validation: applications check file extensions but not actual content, allowing attackers to upload .json files containing base64-encoded pickle data that applications decode and deserialize.

Using eval() or exec() with User-Controlled Strings

Django views, template tags, or business logic use Python's eval() or exec() to execute user-controlled strings as code, enabling arbitrary code execution through query parameters, POST data, or template context variables. Django views that evaluate mathematical expressions from user input: result = eval(request.GET['expression']) executes arbitrary Python code disguised as calculations, allowing attackers to inject import statements, system calls, or object manipulation. Custom template filters or tags using eval() for dynamic functionality: @register.filter def evaluate(value): return eval(value) enables code execution through template contexts when user data flows into filter arguments. Form validation that uses eval() for complex validation rules: if eval(validation_rule): raises ValidationError executes attacker-supplied validation logic during form processing. Django ORM query construction using eval() to dynamically build filter criteria: filter_expr = eval(f"Q({user_filter})") interprets user input as code rather than data. Configuration processing that evaluates string-based settings: DEBUG = eval(os.environ.get('DEBUG', 'False')) executes code from environment variables. API endpoints accepting filter expressions or query DSLs that developers parse using eval(): filters = eval(request.data['filters']) treats JSON data as executable code. Template context processors or middleware that evaluate user session data as code: eval(request.session.get('computed_value')) enables session-based code injection. Django management commands accepting Python expressions as arguments: execute arbitrary code when malicious command-line arguments are passed to eval() within command handlers.

Trusting Serialized Data from External APIs Without Validation

Django applications consume data from external REST APIs, microservices, or third-party integrations that send serialized objects, deserializing API responses without verifying data origin, format, or content integrity. Microservice architectures where Django applications receive pickle-serialized data from internal services: service_response = requests.post(internal_service); data = pickle.loads(service_response.content) trusts internal network services that could be compromised. Integration with legacy systems using pickle for data interchange: external systems send pickled Python objects that Django applications deserialize assuming protocol compatibility without security validation. API gateways or message queues that forward serialized messages: Django consumers deserialize Kafka messages, RabbitMQ payloads, or SQS messages containing pickled data without signature verification. Third-party authentication or session management services providing serialized session data: Django applications trust SSO providers or session stores that return pickled session objects. Webhook handlers receiving serialized payloads from external services: Django webhook endpoints deserialize POST bodies from third-party systems without validating webhook signatures or payload formats. Data synchronization services that exchange serialized model instances: Django applications participating in cross-application data replication deserialize objects from remote systems without integrity checks. Cloud service integrations where managed services provide configuration or state as serialized data: applications deserialize AWS Parameter Store, Azure Key Vault, or GCP Secret Manager responses containing complex objects using unsafe deserialization. Content delivery networks or edge caching layers that serialize and forward application data: Django applications trust CDN-modified responses deserializing cached content without detecting tampering or injection.

Fixes

1

Use JSON Instead of Pickle for All Data Serialization

Replace pickle serialization with JSON throughout Django applications to eliminate code execution risks, using JSON for session storage, caching, API responses, and data interchange while implementing custom serialization for complex types. Configure Django session backend to use JSON serialization: settings.SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer' forces JSON encoding for all session data, preventing pickle-based code execution through session poisoning. Migrate custom session engines from pickle to JSON: replace pickle.dumps(session_dict) with json.dumps(session_dict) and pickle.loads(session_data) with json.loads(session_data) in custom session backend implementations. Configure Django cache framework with JSON serialization: when using django-redis or django-cache-memoize, specify JSON serializers rather than default pickle: CACHES = {'default': {'BACKEND': 'django_redis.cache.RedisCache', 'OPTIONS': {'SERIALIZER': 'django_redis.serializers.json.JSONSerializer'}}} prevents cached object code execution. For complex Python objects requiring serialization, implement explicit to_dict()/from_dict() methods: define methods that convert objects to JSON-serializable dictionaries and reconstruct objects from dictionaries, avoiding pickle's implicit serialization. Use dataclasses with asdict()/from_dict() for structured data: from dataclasses import dataclass, asdict; @dataclass class User: name: str; age: int; json_data = json.dumps(asdict(user_instance)) provides type-safe JSON serialization. Handle datetime objects with ISO format: datetime.isoformat() serializes to strings, datetime.fromisoformat() deserializes, maintaining timezone information safely. Serialize binary data using base64 encoding within JSON: base64.b64encode(binary_data).decode() converts bytes to JSON-compatible strings. Audit codebase for pickle usage: grep -r 'pickle\.' finds all pickle imports and calls requiring migration to JSON alternatives.

2

Use Safe YAML Loaders (yaml.safe_load) to Prevent Code Execution

Replace all yaml.load() calls with yaml.safe_load() or yaml.load(input, Loader=yaml.SafeLoader) to restrict YAML parsing to simple data structures without Python object instantiation or code execution capabilities. Use yaml.safe_load() for all YAML parsing: config = yaml.safe_load(file_content) deserializes YAML to Python primitives (strings, numbers, lists, dicts) without executing !!python tags or constructing arbitrary objects. Specify SafeLoader explicitly when load() is required for compatibility: yaml.load(data, Loader=yaml.SafeLoader) makes security intent clear in code reviews. Validate YAML structure after safe parsing: implement schema validation using libraries like pykwalify or jsonschema to verify loaded YAML matches expected structure before using data. For configuration files, combine safe_load() with configuration validation: load YAML safely, then validate required keys, value types, and ranges using Django's configuration validation or custom validators. Handle YAML parsing errors gracefully: wrap yaml.safe_load() in try/except blocks catching yaml.YAMLError, logging parse failures, and returning safe defaults rather than allowing exceptions to expose system information. Document YAML security in team standards: establish policy requiring safe_load() for all YAML parsing, flagging yaml.load() in code reviews as security violations. Update dependency versions: ensure PyYAML >= 5.1 which changes default behavior but still require explicit safe_load() for clarity. Search codebase for unsafe patterns: grep -r 'yaml\.load[^_]' identifies yaml.load() calls without safe_ prefix requiring remediation. For advanced YAML features requiring custom constructors, use SafeLoader subclasses with explicit constructor registration: class CustomSafeLoader(yaml.SafeLoader): pass; CustomSafeLoader.add_constructor('!custom', custom_constructor) provides controlled deserialization without full code execution.

3

Validate and Sanitize All Input Before Deserialization

Implement comprehensive input validation and sanitization before deserializing any data, using schema validation, type checking, allowlists, and format verification to prevent malicious payloads from reaching deserialization code. Define JSON schemas for expected data structures using jsonschema library: schema = {'type': 'object', 'properties': {'name': {'type': 'string'}, 'age': {'type': 'number'}}, 'required': ['name']}; jsonschema.validate(json.loads(input_data), schema) ensures data matches expectations before processing. Validate file uploads before deserialization: check MIME types using python-magic, verify file sizes with size limits, validate file extensions against allowlists, and scan content for malicious patterns before deserializing. Implement data type validation after deserialization: if not isinstance(deserialized_data, dict): raise ValidationError('Expected dictionary') ensures deserialized data has expected structure. Use Django forms and serializers for API input validation: create DRF serializers or Django forms that validate deserialized data against model constraints, business rules, and security requirements before saving. Sanitize string inputs after deserialization: apply length limits, character allowlists, and content filtering to remove potentially malicious data from deserialized strings. Validate external API responses before deserialization: verify HTTPS certificates, check response status codes, validate Content-Type headers, and inspect response structure before deserializing payloads. Implement allowlist-based validation for enumerated values: if deserialized_value not in ['option1', 'option2', 'option3']: raise ValidationError('Invalid value') restricts data to known-safe values. Log deserialization failures: capture validation errors, deserialization exceptions, and malformed input attempts in Django logs for security monitoring and incident response. Use Django's clean() methods in forms/serializers: override clean_<field>() and clean() to implement custom validation logic that inspects deserialized data before database operations.

4

Implement Cryptographic Signatures for Serialized Data Integrity

Add HMAC or digital signature verification to serialized data to ensure integrity and authenticity, preventing tampering and injection of malicious payloads in session storage, caches, or inter-service communication. Use Django's signing framework for signed serialization: from django.core.signing import dumps, loads; signed_data = dumps(data_dict); original_data = loads(signed_data) uses SECRET_KEY to sign and verify data preventing tampering. Configure Django sessions with signed cookies: settings.SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' stores session data in cryptographically signed cookies that Django verifies before use. Implement HMAC signatures for cache data: import hmac, hashlib; signature = hmac.new(settings.SECRET_KEY.encode(), cached_data, hashlib.sha256).hexdigest(); signed_cache_data = cached_data + b'.' + signature.encode() protects cached objects from poisoning. Verify signatures before deserialization: received_data, received_sig = signed_data.rsplit(b'.', 1); expected_sig = hmac.new(secret_key, received_data, hashlib.sha256).hexdigest(); if not hmac.compare_digest(received_sig.decode(), expected_sig): raise SecurityError('Invalid signature') prevents use of tampered data. Use timestamped signatures to prevent replay attacks: include timestamp in signed data and verify age during validation: signed_data = dumps({'data': obj, 'timestamp': time.time()}); loaded = loads(signed_data, max_age=3600) rejects signatures older than one hour. Implement signature rotation: support multiple signing keys during key rotation periods allowing verification with old and new keys: verify with current key, fall back to previous key, reject if both fail. For external API communication, use asymmetric signatures: sign with private key, verify with public key enabling signature verification without sharing signing capability. Store signing keys securely: use environment variables, Django settings with separate production secrets, or secret management services like AWS Secrets Manager rather than hardcoding keys.

5

Use Django's Built-in Serialization Framework for Model Data

Leverage Django's serialization framework (django.core.serializers) for safe model data export and import, using JSON or XML serializers that don't execute code and provide structure validation. Use Django serializers for data export: from django.core import serializers; json_data = serializers.serialize('json', MyModel.objects.all()) safely exports model instances as JSON without pickle code execution risks. Deserialize with built-in framework: for obj in serializers.deserialize('json', json_data): obj.save() reconstructs model instances from JSON with validation. Configure serialization format explicitly: always specify 'json' or 'xml' formats, never 'python' or custom formats that might use pickle: serializers.serialize('json', queryset, ensure_ascii=False) prevents encoding issues. Use natural keys for relationships: configure get_by_natural_key() and natural_key() methods on models to serialize foreign keys as business identifiers rather than primary keys, improving portability and readability. Validate deserialized objects before saving: for obj in serializers.deserialize('json', data): if obj.object.user == request.user: obj.object.full_clean(); obj.save() ensures objects pass model validation and authorization checks. Implement selective field serialization: use fields parameter to control which model fields serialize: serializers.serialize('json', queryset, fields=['name', 'email']) excludes sensitive fields from exports. Create custom serializers extending Django's base: class SafeJSONSerializer(serializers.JSONSerializer): def get_dump_object(self, obj): # Add extra validation; return super().get_dump_object(obj) provides additional safety checks. Use DRF serializers for API serialization: Django REST Framework serializers provide robust validation, field-level permissions, and safe serialization for API endpoints without deserialization vulnerabilities. Document serialization security: establish guidelines requiring Django's serialization framework for model export/import, prohibiting pickle or custom unsafe serialization.

6

Never Use eval() or exec() with User Input - Use Safe Alternatives

Completely eliminate eval() and exec() from Django applications, replacing them with safe alternatives like ast.literal_eval() for data parsing, operator modules for math, and predefined function mappings for dynamic behavior. Replace eval() for mathematical expressions with safe parsers: use ast.literal_eval() for simple literals or implement restricted expression evaluators using ast module that validate all nodes before evaluation. Use simpleeval or asteval libraries for safe expression evaluation: from simpleeval import simple_eval; result = simple_eval(user_expression, functions={'sqrt': math.sqrt}) provides restricted math evaluation without code execution. For dynamic operations, use dictionaries mapping strings to functions: OPERATIONS = {'add': operator.add, 'subtract': operator.sub}; result = OPERATIONS[user_choice](a, b) provides dynamic behavior without eval(). Implement domain-specific languages (DSLs) for user-defined logic: create parsers for restricted query languages, configuration syntaxes, or rule definitions that compile to safe data structures rather than executable code. Use Django template engine for safe variable substitution: Template(template_string).render(Context(variables)) provides variable expansion without code execution risks unlike eval(f-strings). For configuration evaluation, use explicit type conversion: use int(), float(), bool() for type conversion with try/except handling rather than eval() that executes arbitrary code. Replace exec() for dynamic imports with importlib: importlib.import_module(module_name) safely imports modules from validated names without executing arbitrary code. Implement safe filtering/transformation pipelines: define transformation functions as objects or closures, store in configuration, apply sequentially without eval(): pipeline = [str.upper, str.strip]; result = reduce(lambda x, f: f(x), pipeline, data). Audit codebase for eval/exec usage: grep -r 'eval\|exec' identifies all instances requiring replacement with safe alternatives. Establish coding standards prohibiting eval/exec: configure linting tools like flake8, pylint, or bandit to flag eval/exec as errors in CI/CD pipelines.

Detect This Vulnerability in Your Code

Sourcery automatically identifies django insecure deserialization and many other security issues in your codebase.