File Uploads
Chapter 8 of Fundamentals flagged, but deliberately deferred, two things: output escaping and proper input validation. This chapter closes both gaps, and adds file uploads — one of the highest-risk features a web form can offer, since it involves a visitor placing an actual file onto the server's disk.
htmlspecialchars() — Closing the XSS Gap
If a visitor submits <script>alert('hacked')</script> as their comment, the unsafe version actually runs that script in every other visitor's browser who views the page — a Cross-Site Scripting (XSS) attack. htmlspecialchars() converts <, >, &, and quotes into their safe HTML entity equivalents, so the text displays literally instead of being interpreted as markup.
htmlspecialchars() once when data is first received, then storing the escaped version. This causes problems later (double-escaping, or needing the raw value for non-HTML purposes). The standard practice is to store the original input as-is, and escape it specifically at the point it's about to be echoed into HTML — every single time it's displayed.
Validating Input Properly
filter_var() with PHP's built-in filters handles common validation needs (email format, integer ranges, URLs) far more reliably than hand-written checks — FILTER_VALIDATE_EMAIL correctly handles the genuinely complex rules of what counts as a valid email address, something error-prone to get exactly right manually.
File Uploads — The HTML Side
<form> tag, the file's actual contents are never sent to the server at all — $_FILES would simply be empty, with no obvious error to explain why.
$_FILES — The PHP Side
Handling an Upload Safely
photo.jpg and have the browser report image/jpeg as the type, despite the actual content being something else entirely (like executable PHP code). mime_content_type() inspects the file's real content rather than trusting the claim — checking the genuine type is essential before deciding whether to accept an upload.
../../config.php could, if used unmodified, attempt to overwrite a completely different file outside the intended uploads folder (a "path traversal" attack). Generating a new, safe filename (as with uniqid() above) and discarding the original name entirely avoids this risk altogether.
| Function | What it does |
|---|---|
| htmlspecialchars($s) | Escapes HTML special characters before output — prevents XSS |
| filter_var($val, FILTER) | Validates a value against a built-in rule (email, int range, URL) |
| $_FILES['name'] | Information about an uploaded file (name, type, tmp_name, error, size) |
| mime_content_type($path) | Checks the REAL type of a file's content, not the claimed type |
| move_uploaded_file($tmp, $dest) | Moves an upload from its temporary location to its final destination |
Coding Challenges
Write a script that takes a $_POST['feedback'] value containing HTML special characters (test with a string like "<b>Great site!</b>"), and echoes it twice — once unescaped (commented out as unsafe), and once safely with htmlspecialchars(). Explain in a comment what would happen if the unsafe version were used with a real <script> tag instead.
📄 View solutionWrite a function validateRegistration($email, $age) that uses filter_var() to check both, returning an array of error messages (empty if everything is valid). Test it with one fully valid set of inputs and one fully invalid set.
Write a complete safe file-upload handler for a field named "document" that only allows PDF files (application/pdf) up to 5MB, checks the real MIME type with mime_content_type(), generates a new filename with uniqid(), and moves the file into an "uploads/" folder — echoing clear success or failure messages at each validation step.
📄 View solutionChapter 8 Quick Reference
- htmlspecialchars($s) — escape on OUTPUT, every time, to prevent XSS
- filter_var($val, FILTER_VALIDATE_*) — reliable built-in validation (email, int range, URL)
- enctype="multipart/form-data" — required on the form tag for file uploads to work at all
- $_FILES['field'] — name, type (untrusted!), tmp_name, error, size
- mime_content_type($tmp_name) — checks the file's REAL type, never trust the claimed type
- Never use the original filename directly — generate a new one to avoid path traversal
- move_uploaded_file($tmp, $dest) — finalises the upload to its destination folder
- Next chapter: regular expressions in PHP — preg_match, preg_replace