Creating a Secure File Upload System with PHP: A Complete Guide

Introduction
This article explains how to create a secure file upload handling system using PHP, focusing on security measures and data validation.
Initial Configuration
The first section of upload.php sets up essential configurations
define('UPLOAD_DIR', 'uploads/');
define('MAX_FILE_SIZE', 10 * 1024 * 1024); // 10MB
define('ALLOWED_TYPES', [
'image/jpeg',
'image/png',
'image/gif',
'application/pdf',
'application/msword'
]);
These values control:
- Upload directory location
- Maximum allowed file size
- Permitted file types
Response Handling
The sendResponse()
function handles client communication via JSON
function sendResponse($status, $message, $data = []) {
header('Content-Type: application/json');
http_response_code($status);
echo json_encode([
'status' => $status,
'message' => $message,
'data' => $data
]);
exit;
}
The function:
- Sets JSON header
- Sets HTTP status code
- Returns JSON-formatted data
- Terminates script execution
File Validation
The validateFile()
function performs file validation:
function validateFile($file) {
// Check upload errors
if ($file['error'] !== UPLOAD_ERROR_OK) {
return 'Upload error occurred';
}
// Check file size
if ($file['size'] > MAX_FILE_SIZE) {
return 'File size exceeds limit';
}
// Verify file type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
if (!in_array($mimeType, ALLOWED_TYPES)) {
return 'File type not allowed';
}
return true;
}
Validation includes:
- Upload success verification
- File size check
- MIME type verification
Main Process Flow
- HTTP Method Verification
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
sendResponse(405, 'Method not allowed');
} - Form Data Processing
$title = filter_input(INPUT_POST, 'title', FILTER_SANITIZE_STRING);
$description = filter_input(INPUT_POST, 'description', FILTER_SANITIZE_STRING);
$category = filter_input(INPUT_POST, 'category', FILTER_SANITIZE_STRING); - Upload Directory Creation
if (!file_exists(UPLOAD_DIR)) {
mkdir(UPLOAD_DIR, 0777, true);
} - File Processing and Moving
$fileExtension = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
$fileName = uniqid() . '_' . time() . '.' . $fileExtension;
$uploadPath = UPLOAD_DIR . $fileName;
move_uploaded_file($_FILES['file']['tmp_name'], $uploadPath);
Database Integration
Example database integration structure
try {
$pdo = new PDO("mysql:host=localhost;dbname=your_database", "username", "password");
$stmt = $pdo->prepare("INSERT INTO uploads (filename, title, description, category) VALUES (?, ?, ?, ?)");
$stmt->execute([$fileName, $title, $description, $category]);
} catch (Exception $e) {
if (file_exists($uploadPath)) {
unlink($uploadPath);
}
sendResponse(500, 'Database error: ' . $e->getMessage());
}
Security Measures
The system implements multiple security layers
- MIME type verification
- File size restrictions
- Unique filename generation
- Form data sanitization
- PDO database connections
Implementation Guidelines
For production implementation, consider
- Adjusting configuration settings
- Adding user authentication
- Customizing database structure
- Enhancing error handling
- Implementing logging
Potential system enhancements:
- Image processing capabilities
- Version control system
- File sharing features
- Virus scanning integration
This system provides a foundation for a secure file upload solution that can be customized and expanded based on specific requirements.
<?php
/**
* File Upload Handler
* Process file uploads with validation and error handling
*/
// Set error reporting
error_reporting(E_ALL);
ini_set('display_errors', 1);
// Configuration
define('UPLOAD_DIR', 'uploads/');
define('MAX_FILE_SIZE', 10 * 1024 * 1024); // 10MB
define('ALLOWED_TYPES', [
'image/jpeg',
'image/png',
'image/gif',
'application/pdf',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
]);
/**
* Response helper function
* @param int $status HTTP status code
* @param string $message Response message
* @param array $data Additional data
*/
function sendResponse($status, $message, $data = []) {
header('Content-Type: application/json');
http_response_code($status);
echo json_encode([
'status' => $status,
'message' => $message,
'data' => $data
]);
exit;
}
/**
* Validate uploaded file
* @param array $file $_FILES array element
* @return bool|string True if valid, error message if invalid
*/
function validateFile($file) {
// Check upload errors
if ($file['error'] !== UPLOAD_ERROR_OK) {
return 'Upload error occurred: ' . $file['error'];
}
// Check file size
if ($file['size'] > MAX_FILE_SIZE) {
return 'File size exceeds limit of ' . (MAX_FILE_SIZE / 1024 / 1024) . 'MB';
}
// Check file type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
if (!in_array($mimeType, ALLOWED_TYPES)) {
return 'File type not allowed';
}
return true;
}
// Check if this is a POST request
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
sendResponse(405, 'Method not allowed');
}
// Check if file was uploaded
if (empty($_FILES['file'])) {
sendResponse(400, 'No file uploaded');
}
// Get form data
$title = filter_input(INPUT_POST, 'title', FILTER_SANITIZE_STRING);
$description = filter_input(INPUT_POST, 'description', FILTER_SANITIZE_STRING);
$category = filter_input(INPUT_POST, 'category', FILTER_SANITIZE_STRING);
// Validate required fields
if (empty($title)) {
sendResponse(400, 'Title is required');
}
// Create upload directory if it doesn't exist
if (!file_exists(UPLOAD_DIR)) {
if (!mkdir(UPLOAD_DIR, 0777, true)) {
sendResponse(500, 'Failed to create upload directory');
}
}
// Validate file
$validation = validateFile($_FILES['file']);
if ($validation !== true) {
sendResponse(400, $validation);
}
// Generate unique filename
$fileExtension = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
$fileName = uniqid() . '_' . time() . '.' . $fileExtension;
$uploadPath = UPLOAD_DIR . $fileName;
// Move uploaded file
if (!move_uploaded_file($_FILES['file']['tmp_name'], $uploadPath)) {
sendResponse(500, 'Failed to save uploaded file');
}
// Optional: Save file information to database
try {
// Example database connection and query
/*
$pdo = new PDO("mysql:host=localhost;dbname=your_database", "username", "password");
$stmt = $pdo->prepare("INSERT INTO uploads (filename, original_name, title, description, category) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$fileName, $_FILES['file']['name'], $title, $description, $category]);
*/
// Return success response
sendResponse(200, 'File uploaded successfully', [
'filename' => $fileName,
'path' => $uploadPath,
'title' => $title,
'category' => $category
]);
} catch (Exception $e) {
// If database error occurs, delete uploaded file
if (file_exists($uploadPath)) {
unlink($uploadPath);
}
sendResponse(500, 'Database error: ' . $e->getMessage());
}