Python AWS Lambda Tainted Pickle Deserialization

Critical Risk Deserialization
pythonaws-lambdapickledeserializationcode-executionserverless

What it is

The AWS Lambda function deserializes untrusted pickle data, which can lead to arbitrary code execution. Python's pickle module can instantiate arbitrary classes and call arbitrary functions during deserialization, making it extremely dangerous when processing untrusted data in the Lambda environment.

# Vulnerable: Unsafe pickle deserialization in Lambda import pickle import json import boto3 import base64 def lambda_handler(event, context): # Dangerous: Deserializing user-provided pickle data pickle_data = event.get('data', '') try: # Extremely dangerous: Can execute arbitrary code decoded_data = base64.b64decode(pickle_data) obj = pickle.loads(decoded_data) return { 'statusCode': 200, 'body': json.dumps({'result': str(obj)}) } except Exception as e: return { 'statusCode': 400, 'body': json.dumps({'error': str(e)}) } # Another vulnerable pattern - S3 pickle files def process_s3_pickle(event, context): bucket = event['bucket'] key = event['key'] s3_client = boto3.client('s3') try: # Download pickle file response = s3_client.get_object(Bucket=bucket, Key=key) pickle_data = response['Body'].read() # Dangerous: Deserializing pickle from S3 without validation obj = pickle.loads(pickle_data) # Process the object result = process_object(obj) return {'result': result} except Exception as e: return {'error': str(e)} # Session data deserialization def load_session(event, context): session_id = event.get('session_id', '') # Load session data from database or cache session_data = get_session_from_db(session_id) if session_data: # Dangerous: Pickle deserialization of session data session_obj = pickle.loads(session_data) return {'session': session_obj.__dict__} return {'error': 'Session not found'} # Caching with pickle def get_cached_data(event, context): cache_key = event.get('cache_key', '') # Check cache (Redis, DynamoDB, etc.) cached_data = get_from_cache(cache_key) if cached_data: # Dangerous: Deserializing cached pickle data return pickle.loads(cached_data) # Generate and cache new data data = generate_data() pickled_data = pickle.dumps(data) set_cache(cache_key, pickled_data) return data
# Secure: Safe alternatives to pickle deserialization import json import boto3 import base64 import hmac import hashlib import os from typing import Any, Dict from dataclasses import dataclass, asdict, field from datetime import datetime def lambda_handler(event, context): data_format = event.get('format', 'json') data_content = event.get('data', '') # Use safe formats instead of pickle safe_formats = { 'json': deserialize_json, 'msgpack': deserialize_msgpack, 'signed_json': deserialize_signed_json } if data_format not in safe_formats: return { 'statusCode': 400, 'body': json.dumps({'error': 'Unsupported data format'}) } try: result = safe_formats[data_format](data_content) return { 'statusCode': 200, 'body': json.dumps({'result': result}, default=str) } except Exception as e: return { 'statusCode': 400, 'body': json.dumps({'error': 'Deserialization failed'}) } # Safe JSON deserialization def deserialize_json(data: str) -> Dict[str, Any]: try: # JSON is safe for deserialization obj = json.loads(data) # Validate structure if needed if isinstance(obj, dict): return validate_object_structure(obj) return {'data': obj} except json.JSONDecodeError: raise ValueError('Invalid JSON format') # Signed JSON for integrity def deserialize_signed_json(data: str) -> Dict[str, Any]: try: # Data format: base64(json_data).signature parts = data.split('.') if len(parts) != 2: raise ValueError('Invalid signed data format') encoded_data, signature = parts # Verify signature secret_key = os.environ.get('SIGNING_KEY', '').encode() if not secret_key: raise ValueError('Signing key not configured') expected_signature = hmac.new( secret_key, encoded_data.encode(), hashlib.sha256 ).hexdigest() if not hmac.compare_digest(signature, expected_signature): raise ValueError('Invalid signature') # Decode and parse JSON json_data = base64.b64decode(encoded_data).decode('utf-8') return json.loads(json_data) except Exception as e: raise ValueError('Signed data verification failed') # MessagePack alternative (if available) def deserialize_msgpack(data: str) -> Dict[str, Any]: try: import msgpack decoded_data = base64.b64decode(data) obj = msgpack.unpackb(decoded_data, raw=False, strict_map_key=False) return {'data': obj} except ImportError: raise ValueError('MessagePack not available') except Exception: raise ValueError('Invalid MessagePack format') # Data validation def validate_object_structure(obj: Dict[str, Any]) -> Dict[str, Any]: # Define allowed structure allowed_keys = {'id', 'name', 'value', 'timestamp', 'metadata'} validated = {} for key, value in obj.items(): if key in allowed_keys: # Type validation if key == 'id' and isinstance(value, (int, str)): validated[key] = str(value) elif key == 'name' and isinstance(value, str): validated[key] = value[:100] # Limit length elif key == 'value' and isinstance(value, (int, float)): validated[key] = value elif key == 'timestamp' and isinstance(value, str): # Validate timestamp format try: datetime.fromisoformat(value.replace('Z', '+00:00')) validated[key] = value except ValueError: pass # Skip invalid timestamps elif key == 'metadata' and isinstance(value, dict): validated[key] = {k: v for k, v in value.items() if isinstance(k, str) and isinstance(v, (str, int, float))} return validated # Safe S3 data processing def process_s3_data_secure(event, context): bucket = event.get('bucket', '') key = event.get('key', '') # Validate inputs if not bucket or not key: raise ValueError('Bucket and key required') # Validate file extension allowed_extensions = ['.json', '.csv', '.txt'] if not any(key.endswith(ext) for ext in allowed_extensions): raise ValueError('File type not allowed') s3_client = boto3.client('s3') try: response = s3_client.get_object(Bucket=bucket, Key=key) file_content = response['Body'].read().decode('utf-8') # Process based on file type if key.endswith('.json'): data = json.loads(file_content) return process_json_data(data) elif key.endswith('.csv'): return process_csv_data(file_content) elif key.endswith('.txt'): return process_text_data(file_content) except Exception as e: raise Exception('Failed to process S3 file') # Secure session management @dataclass class SessionData: user_id: str created_at: datetime last_accessed: datetime data: Dict[str, Any] = field(default_factory=dict) def to_dict(self) -> Dict[str, Any]: return asdict(self) @classmethod def from_dict(cls, data: Dict[str, Any]) -> 'SessionData': return cls( user_id=str(data['user_id']), created_at=datetime.fromisoformat(data['created_at']), last_accessed=datetime.fromisoformat(data['last_accessed']), data=data.get('data', {}) ) def load_session_secure(event, context): session_id = event.get('session_id', '') if not session_id: raise ValueError('Session ID required') # Load session as JSON from database session_json = get_session_from_db(session_id) if session_json: try: session_data = json.loads(session_json) session_obj = SessionData.from_dict(session_data) # Update last accessed session_obj.last_accessed = datetime.utcnow() # Save updated session save_session_to_db(session_id, json.dumps(session_obj.to_dict(), default=str)) return {'session': session_obj.to_dict()} except (json.JSONDecodeError, KeyError, ValueError): # Invalid session data delete_session_from_db(session_id) raise ValueError('Invalid session data') raise ValueError('Session not found') # Safe caching with JSON def get_cached_data_secure(event, context): cache_key = event.get('cache_key', '') if not cache_key: raise ValueError('Cache key required') # Validate cache key format if not cache_key.isalnum(): raise ValueError('Invalid cache key format') # Check cache cached_json = get_from_cache(cache_key) if cached_json: try: return json.loads(cached_json) except json.JSONDecodeError: # Invalid cached data, remove it delete_from_cache(cache_key) # Generate new data data = generate_data() # Cache as JSON set_cache(cache_key, json.dumps(data, default=str)) return data # Helper functions (implement based on your infrastructure) def get_session_from_db(session_id: str) -> str: # Implement database lookup pass def save_session_to_db(session_id: str, session_data: str) -> None: # Implement database save pass def delete_session_from_db(session_id: str) -> None: # Implement database delete pass def get_from_cache(key: str) -> str: # Implement cache lookup pass def set_cache(key: str, value: str) -> None: # Implement cache set pass def delete_from_cache(key: str) -> None: # Implement cache delete pass

💡 Why This Fix Works

See fix suggestions for detailed explanation.

Why it happens

AWS Lambda functions invoke Python's pickle.loads() or pickle.load() to deserialize data from Lambda event payloads, API Gateway requests, or user-controlled sources, creating critical code execution vulnerabilities because pickle can instantiate arbitrary classes and call arbitrary methods during deserialization. The pickle module uses __reduce__(), __setstate__(), and __getstate__() magic methods to serialize and reconstruct Python objects, enabling attackers to craft malicious pickled payloads that execute code when deserialized. Lambda functions accepting base64-encoded pickle data from event parameters: pickle.loads(base64.b64decode(event['data'])) enables attackers to inject malicious objects that execute arbitrary code upon deserialization. API Gateway Lambda proxy integrations that receive serialized objects in request bodies and deserialize them using pickle: data = pickle.loads(event['body']) creates direct code execution vectors accessible through public APIs. The pickle protocol supports GLOBAL opcode for importing modules, INST opcode for instantiating objects, and REDUCE opcode for calling functions, combining to enable remote code execution: attackers construct payloads containing os.system('malicious_command') that execute when pickle.loads() processes them. Lambda execution environments provide full Python standard library including subprocess, os, socket, and boto3, all accessible through pickle gadget chains that chain object instantiation and method calls to achieve code execution. Environment variables containing AWS credentials (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN) are accessible to code executed through pickle deserialization, enabling credential theft and AWS service abuse.

Root causes

Using pickle.loads() or pickle.load() on User-Provided Data

AWS Lambda functions invoke Python's pickle.loads() or pickle.load() to deserialize data from Lambda event payloads, API Gateway requests, or user-controlled sources, creating critical code execution vulnerabilities because pickle can instantiate arbitrary classes and call arbitrary methods during deserialization. The pickle module uses __reduce__(), __setstate__(), and __getstate__() magic methods to serialize and reconstruct Python objects, enabling attackers to craft malicious pickled payloads that execute code when deserialized. Lambda functions accepting base64-encoded pickle data from event parameters: pickle.loads(base64.b64decode(event['data'])) enables attackers to inject malicious objects that execute arbitrary code upon deserialization. API Gateway Lambda proxy integrations that receive serialized objects in request bodies and deserialize them using pickle: data = pickle.loads(event['body']) creates direct code execution vectors accessible through public APIs. The pickle protocol supports GLOBAL opcode for importing modules, INST opcode for instantiating objects, and REDUCE opcode for calling functions, combining to enable remote code execution: attackers construct payloads containing os.system('malicious_command') that execute when pickle.loads() processes them. Lambda execution environments provide full Python standard library including subprocess, os, socket, and boto3, all accessible through pickle gadget chains that chain object instantiation and method calls to achieve code execution. Environment variables containing AWS credentials (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN) are accessible to code executed through pickle deserialization, enabling credential theft and AWS service abuse.

Deserializing Pickle Data from External Sources Like APIs or Files

Lambda functions retrieve pickle-serialized data from external APIs, third-party services, or file uploads and deserialize it without signature verification or origin validation, trusting that external sources provide safe data when they may be compromised or malicious. REST API clients that fetch pickle-serialized responses from external services: response = requests.get(external_api); data = pickle.loads(response.content) enables man-in-the-middle attacks or compromised API servers to deliver malicious pickled payloads. Lambda functions processing webhook payloads that contain pickle-serialized data from third-party systems: pickle.loads(event['body']) trusts webhook sources without verifying signatures or validating content. File upload handlers that accept pickle files through API Gateway or S3 uploads and deserialize them for processing: uploaded_data = pickle.load(file_object) enables attackers to upload malicious pickle files containing code execution payloads. Data integration workflows where Lambda functions consume data from external systems, message queues, or data lakes that use pickle serialization: pickle.loads(sqs_message['Body']) trusts message content without validation. Configuration management systems that distribute pickle-serialized configuration to Lambda functions: pickle.loads(config_data) enables configuration poisoning attacks where compromised configuration servers deliver code execution payloads. The pickle protocol lacks integrity protection—no built-in signatures, checksums, or authentication—making it impossible to distinguish legitimate pickled data from malicious payloads without implementing custom verification.

Processing Pickle Data from S3 Objects Without Validation

Lambda functions triggered by S3 events deserialize pickle files from S3 buckets without validating object origin, verifying integrity, or restricting bucket access, enabling attackers who gain S3 write access to deliver code execution payloads through pickle files. S3 event notifications triggering Lambda functions that automatically deserialize uploaded pickle files: s3_object = s3_client.get_object(Bucket=bucket, Key=key); data = pickle.loads(s3_object['Body'].read()) creates automatic code execution when attackers upload malicious pickle files. Data processing pipelines where Lambda functions consume pickle-serialized datasets from S3: large-scale data processing that uses pickle for performance creates widespread code execution risk when data sources are compromised. Machine learning model serving where Lambda functions load pickle-serialized models from S3: scikit-learn, PyTorch, and TensorFlow models saved with pickle enable model poisoning attacks delivering code execution through malicious model files. S3 buckets with overly permissive access policies (public write, broad IAM permissions, misconfigured bucket policies) allow unauthorized users to upload malicious pickle files that trigger Lambda code execution. Cross-account S3 access patterns where Lambda functions deserialize pickle files from S3 buckets owned by other AWS accounts without validating bucket ownership or object integrity. Lambda functions processing S3 batch operations or S3 Inventory reports that include pickle-serialized metadata enable attackers who compromise S3 inventory generation to deliver code execution payloads. The ephemeral nature of Lambda execution makes detection difficult: malicious pickle deserialization executes and completes before CloudWatch logs or security monitoring can respond.

Using Pickle for Session Storage or Caching with Untrusted Input

Lambda functions serialize user session data, application state, or cached results using pickle and store them in DynamoDB, ElastiCache, or S3, then deserialize this data on subsequent requests without verifying that stored pickle data hasn't been tampered with by attackers. Session management implementations that pickle session objects: pickle.dumps(session_obj) serializes user sessions including authentication state, permissions, and user data, storing pickled sessions in DynamoDB or Redis where attackers with database access can modify sessions to inject code execution payloads. Application caching layers that pickle complex Python objects for performance: cache_key = f'user_{user_id}'; cached_data = pickle.dumps(expensive_computation()); redis.set(cache_key, cached_data) enables cache poisoning where attackers inject malicious pickled data. Lambda function state management using pickle to serialize application state between invocations: stateful applications that store pickled objects in S3 or DynamoDB for cross-invocation persistence create code execution risks when state storage is compromised. API rate limiting or throttling implementations that store request metadata as pickled objects: pickle-serialized request history in ElastiCache enables attackers with cache access to inject malicious payloads. Distributed task queues where Lambda functions serialize task parameters using pickle: Celery-style task queues using pickle for task serialization enable task injection attacks delivering code execution through malicious task parameters. Session fixation attacks combined with pickle deserialization where attackers control session IDs and can pre-populate session storage with malicious pickled data that executes when legitimate users access those sessions.

Accepting Serialized Objects Through Lambda Event Parameters

Lambda function event schemas accept serialized object parameters through API Gateway, AppSync, or EventBridge where developers deserialize these parameters using pickle, creating direct code execution vectors through user-controlled event data. API Gateway Lambda proxy integrations that expect serialized objects in request parameters: event['queryStringParameters']['data'] or event['body'] containing base64-encoded pickle data that Lambda functions deserialize without validation. GraphQL resolvers backed by Lambda that accept serialized object arguments: GraphQL mutations or queries with arguments containing pickled data that resolvers deserialize: pickle.loads(base64.b64decode(args['serialized_object'])) enables code execution through GraphQL APIs. AWS AppSync pipeline resolvers where Lambda functions process serialized objects from GraphQL context or previous resolver results: $context.result containing pickled data from earlier pipeline stages creates multi-stage code execution opportunities. EventBridge events with custom event patterns that include pickled data in event detail: Lambda functions subscribed to EventBridge that deserialize event['detail']['data'] assuming event sources are trustworthy when EventBridge allows custom events from various sources. Step Functions state machines passing pickled data between Lambda function invocations: state = pickle.loads(event['state']); result = process(state); return {'state': pickle.dumps(result)} chains code execution across workflow steps. Lambda function aliases and versions where event schemas evolved to accept serialized objects without security review: legacy Lambda functions that accepted JSON evolving to accept pickled objects for backward compatibility without recognizing security implications.

Fixes

1

Never Use Pickle with Untrusted Data - Migrate to JSON or MessagePack

Completely eliminate pickle usage from Lambda functions that process external data, replacing pickle serialization with safe alternatives like JSON, MessagePack, or Protocol Buffers that support only data serialization without code execution capabilities. Replace pickle.dumps()/pickle.loads() with json.dumps()/json.loads() for all data interchange: JSON provides human-readable serialization supporting strings, numbers, booleans, None, lists, and dictionaries without code execution risks. For complex Python types that JSON doesn't support natively, implement explicit serialization methods: use dataclasses with asdict()/from_dict(), create to_dict()/from_dict() methods, or implement JSONEncoder/JSONDecoder subclasses that handle custom types safely. Replace pickle serialization of datetime objects with ISO format strings: datetime.isoformat() and datetime.fromisoformat() provide safe datetime serialization. For binary data, use base64.b64encode()/b64decode() combined with JSON: json.dumps({'binary_data': base64.b64encode(data).decode()}) safely serializes binary content. Implement MessagePack for scenarios requiring compact binary serialization with better performance than JSON: msgpack.packb()/unpackb() provides efficient serialization without pickle's code execution risks. Use Protocol Buffers for strongly-typed data interchange with schema validation: define .proto schemas, generate Python classes, and serialize/deserialize using protobuf methods that guarantee type safety. Audit entire codebase for pickle usage: grep -r 'pickle.load\|pickle.dump' identifies all pickle calls requiring replacement. For machine learning models, use framework-specific safe formats: PyTorch torch.save() with weights_only=True, TensorFlow SavedModel format, ONNX for model interchange—all avoid pickle's arbitrary code execution. Document pickle as forbidden in coding standards and implement automated checks in CI/CD pipelines using tools like Bandit or Semgrep that flag pickle usage.

2

Implement Cryptographic Signatures for Data Integrity Verification

When data serialization other than JSON is required, implement HMAC-based or digital signature verification to ensure serialized data hasn't been tampered with before deserialization, preventing injection of malicious payloads. Use HMAC-SHA256 to sign serialized data before storage: signature = hmac.new(secret_key, serialized_data, hashlib.sha256).hexdigest(); signed_data = serialized_data + b'.' + signature.encode() creates tamper-evident data packages. Store signing keys in AWS Secrets Manager or Systems Manager Parameter Store with SecureString type: secret_key = secretsmanager_client.get_secret_value(SecretId='serialization-key')['SecretString'].encode() retrieves keys securely without hardcoding. Verify signatures before deserialization using constant-time comparison: received_sig, expected_sig = data.split(b'.'); if not hmac.compare_digest(received_sig, expected_sig): raise SecurityError('Invalid signature') prevents timing attacks and ensures integrity. Implement signature rotation: include signature version in signed data format allowing graceful key rotation: signed_data_format = base64(serialized_data) + '.' + version + '.' + signature enables verification with multiple keys during rotation periods. Use AWS Key Management Service (KMS) for cryptographic operations: kms_client.sign(KeyId=key_id, Message=serialized_data, SigningAlgorithm='RSASSA_PKCS1_V1_5_SHA_256') and kms_client.verify() provide managed cryptographic signatures. For cross-account or external data exchange, use asymmetric signatures: private key signs data, public key verifies, preventing secret key distribution: sign with RSA or ECDSA private keys, verify with public keys. Include timestamps and nonces in signed data to prevent replay attacks: signed_payload = {'data': serialized_data, 'timestamp': time.time(), 'nonce': secrets.token_hex(16)} prevents reuse of valid signatures. Implement signature verification at Lambda function entry: validate signatures before any deserialization occurs, fail fast with clear error messages, log signature failures to CloudWatch for security monitoring.

3

Use Safe Serialization Formats That Don't Support Code Execution

Adopt serialization formats designed for data interchange that explicitly prohibit code execution, class instantiation, or arbitrary object deserialization: JSON, MessagePack, Protocol Buffers, Apache Avro, or Cap'n Proto provide safe alternatives to pickle. JSON (JavaScript Object Notation) provides universal data interchange format supported across all platforms: json.loads() only deserializes primitive types (strings, numbers, booleans, null, arrays, objects) without class instantiation or code execution. MessagePack offers compact binary JSON with efficient encoding: msgpack.unpackb(data, raw=False) deserializes data safely with better performance than JSON while maintaining safety guarantees. Protocol Buffers provide strongly-typed serialization with schema validation: define message schemas in .proto files, generate Python classes with protoc, serialize with message.SerializeToString(), deserialize with message.ParseFromString(data) ensuring type safety. Apache Avro supports schema evolution and dynamic typing: define schemas in JSON, use fastavro library for efficient serialization/deserialization with schema validation preventing malformed data. Cap'n Proto offers zero-copy serialization for maximum performance: messages.from_bytes(data) deserializes without parsing overhead while maintaining security. For configuration files, use YAML with safe_load(): yaml.safe_load(config) restricts to simple data structures without Python object instantiation, unlike yaml.load() which enables arbitrary code execution. Implement data validation after deserialization regardless of format: use Pydantic models, JSON Schema validation, or manual type checking to ensure deserialized data matches expected structure. Define serialization abstraction layers: create serialize()/deserialize() functions that encapsulate format selection, allowing format changes without modifying application code. Document supported serialization formats in API specifications and enforce format restrictions at API Gateway or Lambda function entry points.

4

Validate Data Sources and Implement Strict Access Controls

Implement comprehensive source validation and access controls that verify data origin, restrict who can write data to storage systems, and validate data integrity before deserialization, creating defense-in-depth protection against malicious data injection. Implement S3 bucket policies that restrict write access to specific IAM roles: deny public write access, require MFA for delete operations, enable versioning to detect unauthorized modifications. Use S3 Object Lock or Glacier Vault Lock for immutable data storage: prevent deletion or modification of serialized data files, enabling forensic analysis after security incidents. Enable AWS CloudTrail logging for all data access: monitor GetObject, PutObject operations on S3 buckets containing serialized data, alert on anomalous access patterns. Implement VPC endpoints for S3 access: restrict data access to Lambda functions within specific VPCs, prevent public internet access to serialized data. Use S3 access points with dedicated access policies: create access points for different Lambda functions with least-privilege policies restricting which objects each function can access. Enable S3 server-side encryption with KMS: encrypt serialized data at rest, use different KMS keys for different sensitivity levels, implement key policies that restrict decryption to authorized Lambda execution roles. For DynamoDB session/cache storage, implement fine-grained access control: use IAM policies restricting PutItem operations to authorized services, implement DynamoDB Streams for change auditing. Use AWS WAF for API Gateway: implement rate limiting, IP allowlisting, and pattern-based request filtering before requests reach Lambda functions. Implement request signing for cross-service communication: require HMAC signatures on requests between Lambda functions, validate signatures before deserializing request data. Enable GuardDuty for threat detection: monitor for anomalous API calls, data exfiltration attempts, or credential compromise that could lead to malicious data injection.

5

Use AWS Managed Services for Stateful Data Instead of Custom Serialization

Replace custom pickle-based session management and caching with AWS managed services designed for stateful data storage that provide built-in security, scalability, and reliability without code execution risks. Use DynamoDB for session storage with TTL for automatic expiration: store JSON-serialized session data with automatic cleanup, leverage DynamoDB encryption at rest and access control. DynamoDB session structure: {'session_id': 'uuid', 'user_id': 'user_123', 'created_at': timestamp, 'expires_at': timestamp, 'data': {json_object}} stores structured session data safely. Use ElastiCache for Redis or Memcached for caching: store JSON-serialized data with automatic eviction, configure encryption in-transit and at-rest, implement cluster mode for high availability. For distributed state management, use AWS Step Functions with built-in state handling: state machines pass JSON data between steps, provide visual workflow monitoring, automatic retry and error handling. Use Amazon Cognito for user authentication and session management: OAuth2/OIDC flows provide secure session tokens (JWT) without custom pickle serialization. Leverage API Gateway HTTP API JWT authorizers: validate tokens server-side without Lambda function involvement, automatic session validation reduces attack surface. Implement AWS Systems Manager Session Manager for session tracking: leverage AWS-managed session handling for administrative access without custom serialization. For caching expensive computations, use DynamoDB with on-demand capacity: cache results as JSON in DynamoDB items with TTL, leverage DynamoDB Accelerator (DAX) for read performance. Use Amazon S3 for large object caching with lifecycle policies: store cached data as JSON files in S3, implement lifecycle transitions to Glacier for cost optimization, use S3 Select for efficient data retrieval. Implement Lambda layers for shared code and dependencies: replace runtime state sharing via pickle with Lambda layers containing common utilities. Use Lambda environment variables for configuration instead of serialized configuration objects: store configuration as JSON in environment variables or Parameter Store.

6

Implement Restricted Unpickling with Allow-Listed Classes Only

If pickle usage is unavoidable for specific use cases like ML model serving, implement restricted unpickling that limits which classes can be instantiated during deserialization, significantly reducing but not eliminating attack surface. Subclass pickle.Unpickler with custom find_class() method that validates class references: class RestrictedUnpickler(pickle.Unpickler): def find_class(self, module, name): if module == 'numpy.core.multiarray' and name == 'scalar': return getattr(importlib.import_module(module), name); raise pickle.UnpicklingError(f'Class {module}.{name} not allowed') restricts instantiation to specific classes. Define allowed class allowlist explicitly: ALLOWED_CLASSES = {('numpy.core.multiarray', 'scalar'), ('numpy', 'ndarray'), ('builtins', 'dict'), ('builtins', 'list')} limits deserialization to known-safe classes. Implement module restrictions: only allow imports from specific safe modules like numpy, pandas, or standard library without os, subprocess, or socket modules. Use pickle.loads() with RestrictedUnpickler: data = RestrictedUnpickler(io.BytesIO(pickled_data)).load() deserializes with class restrictions enforced. For scikit-learn models, use joblib with custom loaders: joblib.load(model_file, unpickler=RestrictedUnpickler) loads ML models with restrictions. Sign pickle data with HMAC before storage: combine restricted unpickling with signature verification for defense-in-depth: verify signature before unpickling, reject unsigned or invalid data. Implement pickle data sandboxing in separate Lambda functions: isolate pickle deserialization in dedicated Lambda functions with minimal IAM permissions, separate from main application logic. Monitor and log all unpickle operations: log module.name combinations requested during unpickling to CloudWatch, alert on unexpected class instantiation attempts. Implement timeout protection: wrap unpickle operations with timeout decorators preventing infinite loops or resource exhaustion from malicious pickle data. Document that restricted unpickling reduces but doesn't eliminate risk: pickle remains fundamentally unsafe, plan migration path to safe serialization formats.

Detect This Vulnerability in Your Code

Sourcery automatically identifies python aws lambda tainted pickle deserialization and many other security issues in your codebase.