Is It Important to Verify That the Uploaded File Is an Actual Image File

Is it important to verify that the uploaded file is an actual image file?

If you think getimagesize() is a bit too slow (because all uploads are done in super highspeed as we know ;) ) you can try the fileinfo library as well. It inspects at least some bytes within the file. It's pretty fast, I use it every day for hundreds of files in an app that should run speedy and it does.

However, what you don't verify you don't know. So probably first checking extension, ensure a safe filename and a safe store and that they are properly send out to the client.

Before letting any image library touch it (and this should include those on the computers of your site's users), for security reasons the file should be scanned by a virus scanner. That's much more slow compared to getimagesize(), others suggest to take a look into the file for any occurance of <?php as well to prevent uploading as payload. Naturally this includes checking for phar files if inclusion is not prevented via the PHP installations security settings (e.g. by suhosin)

Next to on-demand virus scanning, stored files should be checked from time to time again and again because of formerly unknown exploits.

So part of this is always a background job. But even the on demand real-time checks often do not take that much time unless your application does uploads all the time. You might want to introduce some upload-queue, so the upload is already done but the file get's available to the uploader after the necessary tasks have been run.

How to verify uploaded image is an actual image and not a harmful code?

The first step is to try and determine the image type, which you can do with exif_imagetype, example:

$valid_types = array('jpeg','jpg','gif','png');
if (($type = exif_imagetype($_FILES['image']['tmp_name'])) &&
in_array(ltrim(image_type_to_extension($type), '.'), $valid_types)) {
// image appears to be valid

For further security, you should upload the file to a folder that is not accessible via the browser, and also rename the image. for example:

$upload_path = '/path/to/uploads'; // folder ABOVE www or public_html
$hash = hash_file('sha1', $_FILES['image']['tmp_name']);
$ext = image_type_to_extension($type);
$fname = $hash . $ext;

// save it
if (move_uploaded_file($_FILES['image']['tmp_name'], "$upload_path/$fname")) {
// image was saved

Or, rather than using move_uploaded_file you could attempt to redraw it with the GD library or Imagick and only save the redrawn copy. It is likely if the image contains any errors then those libraries will fail to draw it.

That should be secure enough. Even if a user did manage to exploit some vulnerability they would have no way of executing it unless you're feeding it back to them in a predictable way, but it's still very unlikely.

Verify uploaded file is an image

It is not enough to just check the file extension. I could make a ZIP file, and then just renamed the extension to .jpg and you'd assume it was a valid file.

Image formats do have "magic bytes" at the start of them that you could use to potentially identify that a file is, in fact, an image. This Wikipedia entry here has a list of magic numbers you could check against.

Note also that the content-type can be spoofed.

So you have 2 things you can do. The content-type as specified in the original answer, combined with checking the "magic numbers" of the file. If someone really wants to upload duff data that is not an image and bypassed both these checks, you might have to do something else.

So, you have 3 known guards you can implement:

  1. Does the file have an extension on your whitelist? (.jpg/.jpeg/etc...)
  2. Does the content-type match an entry on your whitelist? (image/jpeg)
  3. Once you've ascertained which kind of file it is via the first 2 steps, do the magic numbers match also?

How to check a uploaded file whether it is an image or other file?

I'm assuming that you're running this in a servlet context. If it's affordable to check the content type based on just the file extension, then use ServletContext#getMimeType() to get the mime type (content type). Just check if it starts with image/.

String fileName = uploadedFile.getFileName();
String mimeType = getServletContext().getMimeType(fileName);
if (mimeType.startsWith("image/")) {
// It's an image.
}

The default mime types are definied in the web.xml of the servletcontainer in question. In for example Tomcat, it's located in /conf/web.xml. You can extend/override it in the /WEB-INF/web.xml of your webapp as follows:

<mime-mapping>
<extension>svg</extension>
<mime-type>image/svg+xml</mime-type>
</mime-mapping>

But this doesn't prevent you from users who are fooling you by changing the file extension. If you'd like to cover this as well, then you can also determine the mime type based on the actual file content. If it's affordable to check for only BMP, GIF, JPG or PNG types (but not TIF, PSD, SVG, etc), then you can just feed it directly to ImageIO#read() and check if it doesn't throw an exception.

try (InputStream input = uploadedFile.getInputStream()) {
try {
ImageIO.read(input).toString();
// It's an image (only BMP, GIF, JPG and PNG are recognized).
} catch (Exception e) {
// It's not an image.
}
}

But if you'd like to cover more image types as well, then consider using a 3rd party library which does all the work by sniffing the file headers. For example JMimeMagic or Apache Tika which support both BMP, GIF, JPG, PNG, TIF and PSD (but not SVG). Apache Batik supports SVG. Below example uses JMimeMagic:

try (InputStream input = uploadedFile.getInputStream()) {
String mimeType = Magic.getMagicMatch(input, false).getMimeType();
if (mimeType.startsWith("image/")) {
// It's an image.
} else {
// It's not an image.
}
}

You could if necessary use combinations and outweigh the one and other.

That said, you don't necessarily need ImageIO#write() to save the uploaded image to disk. Just writing the obtained InputStream directly to a Path or any OutputStream like FileOutputStream the usual Java IO way is more than sufficient (see also Recommended way to save uploaded files in a servlet application):

try (InputStream input = uploadedFile.getInputStream()) {
Files.copy(input, new File(uploadFolder, fileName).toPath());
}

Unless you'd like to gather some image information like its dimensions and/or want to manipulate it (crop/resize/rotate/convert/etc) of course.

How to check that an uploaded file is a valid Image in Django

Good news, you don't need to do this:

class ImageField(upload_to=None, height_field=None, width_field=None, max_length=100, **options)

Inherits all attributes and methods from FileField, but also validates that the uploaded object is a valid image.

https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.ImageField

Determine if uploaded file is image (any format) on MVC

In case it can helps anyone, Here is a static method for HttpPostedFileBase that checks if a given uploaded file is an image:

public static class HttpPostedFileBaseExtensions
{
public const int ImageMinimumBytes = 512;

public static bool IsImage(this HttpPostedFileBase postedFile)
{
//-------------------------------------------
// Check the image mime types
//-------------------------------------------
if (!string.Equals(postedFile.ContentType, "image/jpg", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(postedFile.ContentType, "image/jpeg", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(postedFile.ContentType, "image/pjpeg", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(postedFile.ContentType, "image/gif", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(postedFile.ContentType, "image/x-png", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(postedFile.ContentType, "image/png", StringComparison.OrdinalIgnoreCase))
{
return false;
}

//-------------------------------------------
// Check the image extension
//-------------------------------------------
var postedFileExtension = Path.GetExtension(postedFile.FileName);
if (!string.Equals(postedFileExtension , ".jpg", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(postedFileExtension , ".png", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(postedFileExtension , ".gif", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(postedFileExtension , ".jpeg", StringComparison.OrdinalIgnoreCase))
{
return false;
}

//-------------------------------------------
// Attempt to read the file and check the first bytes
//-------------------------------------------
try
{
if (!postedFile.InputStream.CanRead)
{
return false;
}
//------------------------------------------
// Check whether the image size exceeding the limit or not
//------------------------------------------
if (postedFile.ContentLength < ImageMinimumBytes)
{
return false;
}

byte[] buffer = new byte[ImageMinimumBytes];
postedFile.InputStream.Read(buffer, 0, ImageMinimumBytes);
string content = System.Text.Encoding.UTF8.GetString(buffer);
if (Regex.IsMatch(content, @"<script|<html|<head|<title|<body|<pre|<table|<a\s+href|<img|<plaintext|<cross\-domain\-policy",
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Multiline))
{
return false;
}
}
catch (Exception)
{
return false;
}

//-------------------------------------------
// Try to instantiate new Bitmap, if .NET will throw exception
// we can assume that it's not a valid image
//-------------------------------------------

try
{
using (var bitmap = new System.Drawing.Bitmap(postedFile.InputStream))
{
}
}
catch (Exception)
{
return false;
}
finally
{
postedFile.InputStream.Position = 0;
}

return true;
}
}

Edit 2/10/2017: According to a suggested edit, added a finally statement to reset the stream, so we can use it later.

the most reliable way to check upload file is an image

finfo_* library would be good but it will work with >= 5.3.0 versions,

AND getimagesize() GD library function that is return image info WxH and size

if image invalid then getimagesize() show warning so better to use to validate image using finfo_* function,

you can also do for cross version code, see below sample code

<?php 
$file = $_FILES['photo'];
$whitelist_type = array('image/jpeg', 'image/png','image/gif');
$error = null;
if(function_exists('finfo_open')){ //(PHP >= 5.3.0, PECL fileinfo >= 0.1.0)
$fileinfo = finfo_open(FILEINFO_MIME_TYPE);

if (!in_array(finfo_file($fileinfo, $file['tmp_name']), $whitelist_type)) {
$error[] = "Uploaded file is not a valid image";
}
}else if(function_exists('mime_content_type')){ //supported (PHP 4 >= 4.3.0, PHP 5)
if (!in_array(mime_content_type($file['tmp_name']), $whitelist_type)) {
$error[] = "Uploaded file is not a valid image";
}
}else{
if (!@getimagesize($file['tmp_name'])) { //@ - for hide warning when image not valid
$error[] = "Uploaded file is not a valid image";
}
}

file upload: check if valid image

Firstly add accept="image/*" on your input, to accept only image files

<input type="file" name="uploadPicture" accept="image/*" onChange="validateAndUpload(this);"/>

Second, you can create image object to see if it is true image

function validateAndUpload(input){
var URL = window.URL || window.webkitURL;
var file = input.files[0];

if (file) {
var image = new Image();

image.onload = function() {
if (this.width) {
console.log('Image has width, I think it is real image');
//TODO: upload to backend
}
};

image.src = URL.createObjectURL(file);
}
};​


Related Topics



Leave a reply



Submit