PHP

Implementing Secure File Uploads in PHP

Secure your web application by implementing robust server-side file upload validation, including type checks, size limits, and safe storage practices in PHP.

<?php
// Define upload directory outside the web root for security
define('UPLOAD_DIR', __DIR__ . '/../uploads_secure/'); // One level up from script's directory

// Ensure upload directory exists and is writable
if (!is_dir(UPLOAD_DIR)) {
    mkdir(UPLOAD_DIR, 0755, true);
}

function handleSecureFileUpload(array $file): array {
    $response = ['success' => false, 'message' => ''];

    // Check for upload errors
    if ($file['error'] !== UPLOAD_ERR_OK) {
        $response['message'] = 'File upload error: ' . $file['error'];
        return $response;
    }

    // 1. Validate file size
    $maxFileSize = 5 * 1024 * 1024; // 5 MB
    if ($file['size'] > $maxFileSize) {
        $response['message'] = 'File is too large. Maximum size is ' . ($maxFileSize / (1024 * 1024)) . 'MB.';
        return $response;
    }

    // 2. Validate file type (MIME type and extension)
    $allowedMimeTypes = ['image/jpeg', 'image/png', 'application/pdf'];
    $allowedExtensions = ['jpg', 'jpeg', 'png', 'pdf'];

    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mimeType = finfo_file($finfo, $file['tmp_name']);
    finfo_close($finfo);

    $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));

    if (!in_array($mimeType, $allowedMimeTypes) || !in_array($extension, $allowedExtensions)) {
        $response['message'] = 'Invalid file type. Only JPEG, PNG, and PDF are allowed.';
        return $response;
    }

    // 3. Generate a unique and safe filename
    $newFileName = uniqid('', true) . '.' . $extension;
    $targetFilePath = UPLOAD_DIR . $newFileName;

    // 4. Move the uploaded file to a secure directory
    if (move_uploaded_file($file['tmp_name'], $targetFilePath)) {
        // Set restrictive permissions on the uploaded file
        chmod($targetFilePath, 0644); // User read/write, group read, others read

        $response['success'] = true;
        $response['message'] = 'File uploaded successfully.';
        $response['filePath'] = $targetFilePath; // Store only the path, not web-accessible URL
    } else {
        $response['message'] = 'Failed to move uploaded file.';
    }

    return $response;
}

// Example Usage (assuming $_FILES['user_file'] exists from a form submission)
// if (isset($_FILES['user_file'])) {
//     $uploadResult = handleSecureFileUpload($_FILES['user_file']);
//     if ($uploadResult['success']) {
//         echo "Upload successful: " . $uploadResult['filePath'];
//     } else {
//         echo "Upload failed: " . $uploadResult['message'];
//     }
// } else {
//     // Handle no file uploaded scenario
// }
?>
How it works: This PHP snippet outlines crucial steps for secure file uploads. It validates file size and MIME type to prevent oversized or malicious executable files. It generates a unique filename to avoid overwriting or path traversal issues and stores files outside the web root for enhanced security. Finally, it sets restrictive permissions on the uploaded file.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs