PHP
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Object-Oriented PHP
-
Section 5: Working with Data
-
Section 6: Modern PHP (PHP 8)
-
Section 7: Working with Files and Networking
-
Section 8: Common PHP Frameworks Overview
-
Section 9: Tooling and Ecosystem
-
Section 10: Practical Projects
-
Section 11: Interview Practice
-
Section 12: More Standard Library
-
Section 13: Data Structures and Algorithms in PHP
-
Section 14: More Practice Exercises
-
Section 15: More Security and Best Practices
-
Section 16: More Testing and Tooling
-
Section 17: WordPress-Style CMS Concepts
-
Section 18: Advanced OOP Practice
-
Section 19: More Web Fundamentals
-
Section 20: Database Practice
-
Section 21: PHP Manual: Array Functions
-
Section 22: PHP Manual: Date and Calendar Functions
-
Section 23: PHP Manual: Filesystem and Directory Functions
-
Section 24: PHP Manual: Filter and Var Handling
-
Section 25: PHP Manual: Math Functions
-
Section 26: PHP Manual: JSON and XML
-
Section 27: PHP Manual: Network and Stream Functions
-
Section 28: PHP Manual: Error and Exception Handling
-
Section 29: PHP Manual: Output Control and Misc
-
Section 30: PHP Manual: FTP, Zip, and Mail
-
Section 31: Modern PHP Frameworks Deep Dive
-
Section 32: PHP Design Patterns
-
Section 33: More Practice Exercises
-
Section 34: PHP Performance and Deployment
-
Section 35: More Interview Practice
-
Section 36: More PHP Standard Library
-
Section 37: PHP Concurrency and Async
-
Section 38: More Web Development Practice
-
Section 39: PHP Testing Deep Dive
-
Section 40: Composer and Package Development
-
Section 41: PHP Security Deep Dive
-
Section 42: More Practical Projects
-
Section 43: Legacy PHP Maintenance
-
Section 44: More Algorithm Practice
-
Section 45: Final Practice and Review
-
Section 46: PHP for E-Commerce Patterns
-
Section 47: PHP API Design Deep Dive
-
Section 48: PHP Caching Strategies
-
Section 49: PHP Queue and Background Jobs
-
Section 50: PHP Multi-Tenancy Patterns
-
Section 51: PHP Real-Time Features
-
Section 52: PHP CMS and Content Modeling
-
Section 53: PHP Internationalization
-
Section 54: More Framework-Specific Practice
-
Section 55: PHP Legacy Code Refactoring
-
Section 56: More Practice Projects Round 2
-
Section 57: PHP Command-Line Applications
-
Section 58: PHP and Microservices
-
Section 59: More Interview and Review Round 2
27: File Uploads and Handling
When you first start handling file uploads in PHP, it's tempting to treat the $_FILES superglobal as a reliable source of truth. You see a filename, you see a temporary path, and your instinct is to just move that file from the temporary folder to a permanent one. I've seen countless junior devs—and honestly, a few seniors in a rush—write code that looks something like this:
$target = "uploads/" . $_FILES['profile_pic']['name'];
move_uploaded_file($_FILES['profile_pic']['tmp_name'], $target);
Trusting the Client (and Why That's a Disaster)
On the surface, this works. You upload my_cat.jpg, it lands in the uploads folder, and you're happy. But here is the problem: you are trusting the user. In the world of web security, trusting the client is the fastest way to get your server compromised. The ['name'] key in the $_FILES array is provided by the browser, which means a malicious user can change it to whatever they want using a simple proxy tool.
Imagine someone uploads a file named ../../../index.php. If your server permissions are loose enough, they've just overwritten your homepage. Or worse, they upload shell.php, disguise it as an image, and then navigate to /uploads/shell.php to execute arbitrary code on your machine. I can't stress this enough: never, ever use the user-provided filename directly in your file system paths.
Sanitization and Strategic Storage
To do this right, we have to stop thinking about the filename as "the name of the file" and start thinking about it as "a suggestion that we should mostly ignore." The better approach is to generate your own unique filename and strictly validate the file's content, not just its extension.
Instead of trusting .jpg at the end of a string, use finfo_file or mime_content_type to look at the actual magic bytes of the file. If the user says it's a JPEG but the server sees it's a PHP script, you drop it immediately. Then, instead of keeping the original name, generate a hash or a UUID. This prevents filename collisions (two people uploading image.jpg) and kills the path traversal attack entirely.
$uploadDir = '/var/www/uploads/';
$fileTmpPath = $_FILES['profile_pic']['tmp_name'];
// Check the actual MIME type
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($fileTmpPath);
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
if (!in_array($mimeType, $allowedTypes)) {
die("Sorry, only images are allowed.");
}
// Generate a random name to avoid collisions and security risks
$extension = pathinfo($_FILES['profile_pic']['name'], PATHINFO_EXTENSION);
$newFileName = bin2hex(random_bytes(16)) . '.' . $extension;
$destPath = $uploadDir . $newFileName;
if (move_uploaded_file($fileTmpPath, $destPath)) {
echo "File uploaded successfully as " . $newFileName;
}
The Cost of Security
Now, the trade-off here is complexity and "user experience" in the backend. By renaming the file, you lose the original context. If a user uploads vacation_in_italy.jpg and you save it as a1b2c3d4...jpg, you can't just look at your folder to see what's what. You'll need a database table to map the random filename back to the original name or the user ID.
There is also the issue of storage location. If you put your uploads folder inside your public web root, you're still potentially exposing yourself to execution attacks if your server isn't configured to disable script execution in that specific directory. The gold standard is to store files outside the public folder (above public_html or www) and serve them through a PHP proxy script that reads the file and sends the correct headers. It's a bit more work, but it's the difference between a professional application and a security liability.
📋 Practical Task
Build a Secure Profile Avatar Uploader
Create a PHP script that allows a user to upload a profile picture. Your implementation must meet the following security requirements:
- MIME Validation: Use
finfo` ormime_content_typeto ensure the file is actually an image (JPEG, PNG, or GIF). Do not rely on the file extension. - Filename Randomization: Rename the uploaded file to a random string (using
random_bytesoruniqid) to prevent overwriting and path traversal attacks. - Size Limit: Implement a check to ensure the file is no larger than 2MB.
- Storage: Save the file into a directory named
uploads/, ensuring the script handles the case where the directory doesn't exist yet.
Test your script by attempting to upload a .txt file renamed to .jpg to verify that your MIME check catches the deception.
There are no comments for now.