Security . 30 Jul 2025 . By Nessa

The Image That Steals

It all starts with a seemingly harmless feature - uploading a profile picture. This functionality exists in nearly every modern web application. On the surface, it looks simple: the form only accepts .jpg, .png, or .jpeg files, and there's client-side validation in place. Everything seems secure.

But it's exactly in these everyday features where vulnerabilities often hide - waiting for someone to stop playing by the rules.

One of the most overlooked entry points in web security is file upload - and attackers have developed multiple techniques to bypass restrictions. Among the fundamental methods are:

- Extension-based bypasses, which include tricks like using special characters in filenames or double extensions (e.g., .php.jpg).

- Content-Type manipulation, where headers are forged to mislead server-side checks.

- Magic byte exploitation, where the actual file content mimics expected formats, fooling systems that rely on superficial file signature checks.

In this article, we’ll focus on the method - bypasses based on file extensions and special character abuse - and explore how something as innocent as an image upload can lead to full system compromise.


Disguised as a harmless image

What happens when someone uploads a file named profile.jpg.html?

The frontend happily accepts it. The extension matches the allowed pattern. The file is sent to the server, stored in a public /uploads/ folder - and that's the end of the check.

But when this file is opened in a browser, instead of displaying an image, it renders as a full HTML page, because the content is HTML. Worse yet, it may contain a payload like this:

Now imagine: a moderator or admin opens the user's profile and views the uploaded image - unknowingly triggering a malicious script. Session cookies are silently sent to an external domain. Access is compromised. No alerts, no warnings, just a stolen session behind a fake image.


Why it works?

This is a modern take on a classic file upload vulnerability - no RCE, no SQL injection, just trust in something as fragile as a file extension.

Browsers perform what's known as MIME-sniffing - they inspect the file's content and guess its type. If the server doesn't explicitly instruct them not to do this (X-Content-Type-Options: nosniff), the browser may decide that your .jpg is actually HTML, and treat it accordingly.

From there, it's just a short step to XSS, phishing, or other injection-based attacks.


How attackers test for it

Security testers typically try uploading:

image.jpg.html — with embedded HTML/JS;

image.jpg.php — to see if PHP is executed;

image.jpg — with HTML inside the file.

They then try to access the uploaded file via its direct URL. If the browser renders it as a web page - the vulnerability is confirmed.


How to defend against it

To effectively counter HTML-based file upload bypasses, robust server-side security measures are paramount. Focus on these key technical controls:

1. Rigorous Server-Side File Validation

- Don't trust file extensions.

Never rely on the client-provided file extension.

- Deep MIME type checks.

Inspect the file's true content using "magic bytes" with libraries like libmagic (e.g., finfo_file( ) in PHP, pyton-magic in Python) to confirm its actual MIME type.

- Image Integrity Validation.

For image uploads, attempt to load the file with an image processing library (e.g., GD, Pillow). Failure to load or detect inconsistencies can flag malicious content.


2. Sanitize and Normalize Uploaded Files

- Server-Side Image Re-encoding.

Always re-encode images after validation using tools like ImageMagick or Pillow. This strips malicious scripts, embedded metadata (like EXIF), and creates a clean, pixel-only image.

- Generate New, Random Filenames.

Store files with cryptographically secure, random names (e.g., UUIDs) to prevent path traversal and naming conflicts.


3. Secure File Storage and Serving

- Store Outside Web Root.

Keep uploaded files in a directory that is not directly accessible via the web server (e.g., outside the public_html folder or in cloud storage).

- Controlled Access Endpoints.

Serve files through a dedicated server-side script or controller that enforces access checks and sets proper HTTP headers, e.g., /get_image.php?id=123 instead of direct access to /uploads/image.jpg


4. Implement Crucial Security Headers

X-Content-Type-Options: nosniff

This header is vital. It instructs browsers to disable MIME-sniffing and strictly adhere to the Content-Type header provided by the server. Include it in all responses serving user-uploaded content.

Content-Disposition: attachment; filename=”your_safe_file.jpg”

Force browsers to download the file rather than rendering it directly. This is a strong defense, especially for files that shouldn't be executed in the browser.

Explicit Content-Type

Always explicitly set the correct Content-Type header (e.g., image/jpeg) based on your server-side validation, not user input.


5. Restrict Directory Execution Permissions

Configure web server settings (e.g., Apache's php_flag engine off or Nginx deny all) to prevent script execution in upload directories. This ensures that even if a malicious script is uploaded, it cannot be run by the server.


Final thoughts

This technique isn’t new - but it still works, even in 2025. Many systems prioritize convenience over scrutiny, and developers continue to place too much trust in the frontend.

All it takes is a single "image" that’s not what it claims to be, and suddenly, your application becomes the attacker’s playground.

For teams seeking more advanced protection, the a42.tech platform delivers comprehensive vulnerability assessments, automated scanning, and SOC-integrated detection workflows - providing both offensive and defensive teams with the visibility they need to stay ahead of evolving threats.



Test smarter. Detect faster. Stay ahead.

Don’t just keep up with trends - be prepared for them!

Test our platform and see how fast detection meets real security.