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

12

70

So I'm using this code for view:

<form action="" method="post" enctype="multipart/form-data">

  <label for="file">Filename:</label>
  <input type="file" name="file" id="file" />

  <input type="submit" />
</form>

This for model:

[HttpPost]
public ActionResult Index(HttpPostedFileBase file) {

  if (file.ContentLength > 0) {
    var fileName = Path.GetFileName(file.FileName);
    var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
    file.SaveAs(path);
  }

  return RedirectToAction("Index");
}

Works great unless the user add a file which isn't an image. How can I assure the file uploaded is an image. Thanks

Germany answered 16/6, 2012 at 13:37 Comment(5)
there are a lot of image formats.Elusion
@DanielA.White That's precisely the point of my question. Determine that it's any image format but an image after all.Germany
You can check the ContentType of file as a rudimentary form of validation. If that's not good enough, you can try and peek at the "header" of the file stream and see if it matches any of the types of images you with to support, ala #211150Krystin
DO THIS PROPERLY and use Binary Analysis for Mime Type detection.Anodize
Check this: codingfusion.com/Post/…Eventful
F
126

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.

Flowerage answered 29/1, 2013 at 16:43 Comment(8)
What's your idea for these file types: ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pps", ".ppsx", please.Ley
Besides from validating the file extension, you can validate the first bytes as well (see for example serverfault.com/questions/338087/…). In this regard, please note that they're compressed ZIP (so remember the mime-type) files, which gives you an XML, you can try to validate this XML format, though I think that it'll hard unlike the image validation above. An idea that might work is to use the Office COM and try to load the documents to see if you got any errors. This approach, though, won't work in all environments.Flowerage
Can you modify this to allow for ASP.NET Core as there is no drawing or bitmap class? Thanks a lotPsalms
if (postedFile.ContentLength > ImageMinimumBytes) This is a typo no? It should read if (ContentLength < ImageMinimumBytes)Hertha
I have corrected the bug/typo mentioned by TomSelleck in my adaption of this answer for 2018 using .NET CORE 2.1 (see below).Awfully
You're absolutely right, @Hertha . Fixed here as well.Flowerage
@Flowerage Thanks for posting, proved very useful when I needed it!Hertha
@OzB: Compare what "exceeds the size limit" means to what your code does now.Katheryn
C
49

It's 2018 and the accepted answer does not work with .NET CORE 2.1 because we now have IFormFile instead of HttpPostedFileBase.

Here comes the adaption of the accepted answer to .NET CORE 2.1 (I also fixed the bug/typo mentioned by TomSelleck in his comment to the accepted answer):

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

    public static bool IsImage(this IFormFile postedFile)
    {
        //-------------------------------------------
        //  Check the image mime types
        //-------------------------------------------
        if (postedFile.ContentType.ToLower() != "image/jpg" &&
                    postedFile.ContentType.ToLower() != "image/jpeg" &&
                    postedFile.ContentType.ToLower() != "image/pjpeg" &&
                    postedFile.ContentType.ToLower() != "image/gif" &&
                    postedFile.ContentType.ToLower() != "image/x-png" &&
                    postedFile.ContentType.ToLower() != "image/png")
        {
            return false;
        }

        //-------------------------------------------
        //  Check the image extension
        //-------------------------------------------
        if (Path.GetExtension(postedFile.FileName).ToLower() != ".jpg"
            && Path.GetExtension(postedFile.FileName).ToLower() != ".png"
            && Path.GetExtension(postedFile.FileName).ToLower() != ".gif"
            && Path.GetExtension(postedFile.FileName).ToLower() != ".jpeg")
        {
            return false;
        }

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

            byte[] buffer = new byte[ImageMinimumBytes];
            postedFile.OpenReadStream().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.OpenReadStream()))
            {
            }
        }
        catch (Exception)
        {
            return false;
        }
        finally
        {
            postedFile.OpenReadStream().Position = 0;
        }

        return true;
    }
}
Centaur answered 16/8, 2018 at 14:49 Comment(5)
I'm using .net core 3.1 and I had to add reference the System.Drawing.Common library for this to workWellington
does this work on .net core on docker in ubuntu, Im getting some issues, looking into it it might also be reverse proxy its behind,Wellington
@MaxCarroll I have not tried it on docker in ubuntu. I guess if you are "getting some issues" it's best to open a new question and give a description of those issues.Awfully
I'd suggest that this would be a lot better if it returned true for each condition - unless you want to check everything?Infidel
same as @MaxCarroll, this breaks on Fargate. The issue is in System.Drawing: github.com/dotnet/runtime/issues/21980Pyxie
O
19

For anyone that runs into this.

You could also use a file.ContentType.Contains("image") to check if the content type is of image/*.

if(file.ContentLength > 0 && file.ContentType.Contains("image"))
{
    //valid image
}
else
{
    //not a valid image
}

Not sure if this is best practice, but it works for me.

Oloroso answered 4/3, 2016 at 21:25 Comment(6)
This also perfectly valid in my case (well, IFormFile does not contain ContentLength but Length property)Yates
This should be the accpeted answerClobber
If uploading a file, check the headers of the MultipartFileData if (file.Headers.ContentLength > 0 && file.Headers.Contains("image"))Clobber
this code only validates file name. it does NOT validate the file is an image.Ernaernald
@EdwardOlamisan, are you talking about the answer or the comment above your comment? My code reviews the contentType which is not the filenameOloroso
@ShawnGavett sorry, my mistake, I copy-pasted my comment to another answer because they are so similar: https://mcmap.net/q/277553/-determine-if-uploaded-file-is-image-any-format-on-mvc. Bottom line, this answer has the same problem: file name and headers, including ContentType, are provided by the user and can be crafted by a malicious user.Ernaernald
U
15

Don't have the compiler at hand but something like this should do:

try
{
   var bitmap = Bitmap.FromStream( file.InputStream );
   // valid image stream
}
catch 
{
    // not an image
}
Unprepared answered 16/6, 2012 at 13:40 Comment(0)
C
11

Use in static helper class:

public static bool IsImage(HttpPostedFileBase postedFile)
    {
        try  {
              using (var bitmap = new System.Drawing.Bitmap(postedFile.InputStream))
                    {                        
                            return !bitmap.Size.IsEmpty;
                    }
                }
                catch (Exception)
                {
                    return false;
                }
            }
    }

Use in ASP.NET MVC viewmodel:

public class UploadFileViewModel
    {
        public HttpPostedFileBase postedFile { get; set; }

        public  bool IsImage()
        {
            try  {
                  using (var bitmap = new System.Drawing.Bitmap(this.postedFile.InputStream))
                        {                        
                                return !bitmap.Size.IsEmpty;
                        }
                    }
                    catch (Exception)
                    {
                        return false;
                    }
                }
        }
    }

This example checks to see whether the image is a real image, and you can modify and convert it.

It eats memory as an example of six-liter V8, so it should be used when you really want to know what this image.

Checked answered 27/12, 2013 at 14:0 Comment(2)
please provide a little bit of explanation too. :DAvens
i think this is the only solution that can be trustedAncilla
S
2

Implementation in much more cleaner way,

public static class FileExtensions
{
    private static readonly IDictionary<string, string> ImageMimeDictionary = new Dictionary<string, string>
    {
        { ".bmp", "image/bmp" },
        { ".dib", "image/bmp" },
        { ".gif", "image/gif" },
        { ".svg", "image/svg+xml" },
        { ".jpe", "image/jpeg" },
        { ".jpeg", "image/jpeg" },
        { ".jpg", "image/jpeg" },
        { ".png", "image/png" },
        { ".pnz", "image/png" }
    };

    public static bool IsImage(this string file)
    {
        if (string.IsNullOrEmpty(file))
        {
            throw new ArgumentNullException(nameof(file));
        }

        var extension = Path.GetExtension(file);
        return ImageMimeDictionary.ContainsKey(extension.ToLower());
    }
}
Spruik answered 30/12, 2016 at 11:55 Comment(1)
this code only validates file name. it does NOT validate the file is an image.Ernaernald
A
2

It doesn't answer the question how to check if an uploaded file is an image on the server.

However, the original problem statement appears more to be that users are accidentally uploading the wrong file.

In which case, a very easy solution is to set the accept attribute on the input element instead.

<input type="file" id="file" accept="image/*">

The usual caveats about trusting user input applies.

Airlift answered 2/3, 2021 at 9:39 Comment(0)
S
1

For IFormFile : It is based on a logic that if .NET can treat the file as a valid image and can be processed further, then it is a valid image.

using System.Drawing;

    private bool IsValidImageFile(IFormFile file) {
    
      try {
        var isValidImage = Image.FromStream(file.OpenReadStream());
      } catch {
        return false;
      }
    
      return true;
    }
Substituent answered 8/4, 2019 at 19:15 Comment(0)
E
0

As a first step, you should form a white list around the acceptable MIME types against the ContentType property.

Elusion answered 16/6, 2012 at 13:42 Comment(1)
That won't work for crafted requests where mime type does not correspond to the stream content.Unprepared
D
0
public static ImageFormat GetRawImageFormat(byte[] fileBytes)
{
    using (var ms = new MemoryStream(fileBytes))
    {
        var fileImage = Image.FromStream(ms);
        return fileImage.RawFormat;
    }
}

Usage:

if (GetRawImageFormat(fileBytes).IsIn(ImageFormat.Jpeg, ImageFormat.Png, ImageFormat.Gif))
    {
        //do somthing
    }
Deaconry answered 2/12, 2018 at 2:40 Comment(0)
M
0

Correct answer is:

        public bool CheckTrueImageType()
    {
        // DICTIONARY OF ALL IMAGE FILE HEADER
        Dictionary<string, byte[][]> imageHeader = new Dictionary<string, byte[][]>();
        imageHeader.Add("JPG", new byte[][] { new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }, 
                                              new byte[] { 0xFF, 0xD8, 0xFF, 0xE1 }, 
                                              new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 }, 
                                              new byte[] { 0xFF, 0xD8, 0xFF, 0xE3 }, 
                                              new byte[] { 0xFF, 0xD8, 0xFF, 0xE8 }, 
                                              new byte[] { 0xFF, 0xD8, 0xFF, 0xDB } });
        imageHeader.Add("JPEG", new byte[][] { new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }, 
                                               new byte[] { 0xFF, 0xD8, 0xFF, 0xE1 }, 
                                               new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 }, 
                                               new byte[] { 0xFF, 0xD8, 0xFF, 0xE3 }, 
                                               new byte[] { 0xFF, 0xD8, 0xFF, 0xE8 }, 
                                               new byte[] { 0xFF, 0xD8, 0xFF, 0xDB }  });
        imageHeader.Add("PNG", new byte[][] { new byte[] { 0x89, 0x50, 0x4E, 0x47 } });
        imageHeader.Add("TIF", new byte[][] { new byte[] { 0x49, 0x49, 0x2A, 0x00 }, 
                                              new byte[] { 0x49, 0x20, 0x49 }, 
                                              new byte[] { 0x4D, 0x4D, 0x00, 0x2A }, 
                                              new byte[] { 0x4D, 0x4D, 0x00, 0x2B } });
        imageHeader.Add("TIFF", new byte[][] { new byte[] { 0x49, 0x49, 0x2A, 0x00 }, 
                                               new byte[] { 0x49, 0x20, 0x49 }, 
                                               new byte[] { 0x4D, 0x4D, 0x00, 0x2A }, 
                                               new byte[] { 0x4D, 0x4D, 0x00, 0x2B } });
        imageHeader.Add("GIF", new byte[][] { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, 
                                              new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 } });
        imageHeader.Add("BMP", new byte[][] { new byte[] { 0x42, 0x4D } });
        imageHeader.Add("ICO", new byte[][] { new byte[] { 0x00, 0x00, 0x01, 0x00 } });

        bool isTrueImage = false;
        if (FileUpload1.HasFile)
        {
            // GET FILE EXTENSION
            string fileExt = FileUpload1.FileName.Substring(FileUpload1.FileName.LastIndexOf('.') + 1).ToUpper();

            // CUSTOM VALIDATION GOES HERE BASED ON FILE EXTENSION IF ANY
            
            byte[][] tmp = imageHeader[fileExt];

            foreach (byte[] validHeader in tmp)
            {
                byte[] header = new byte[validHeader.Length];

                // GET HEADER INFORMATION OF UPLOADED FILE
                FileUpload1.FileContent.Seek(0, System.IO.SeekOrigin.Begin);
                FileUpload1.FileContent.Read(header, 0, header.Length);

                if (CompareArray(validHeader, header))
                {
                    // VALID HEADER INFORMATION 
                    isTrueImage = true;
                    break;
                }
            }
        }

        if (!isTrueImage)
        {
            lblStatus.ForeColor = System.Drawing.Color.Red;
            lblStatus.Text += "<br />Invalid file header! ";
        }

        return isTrueImage;
    }

    private bool CompareArray(byte[] a1, byte[] a2)
    {
        if (a1.Length != a2.Length)
            return false;

        for (int i = 0; i < a1.Length; i++)
        {
            if (a1[i] != a2[i])
                return false;
        }

        return true;
    }
Mellon answered 9/2, 2023 at 22:58 Comment(2)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Pankhurst
Best to quote your sources. Looks like the code is derived from this.Benison
A
-2

in the server side compare with content type if its matching with your required uploaded format then proceed or else return error message

Ahmed answered 14/11, 2014 at 6:57 Comment(1)
This will not restrict users from selecting other MIME types.Pickel

© 2022 - 2024 — McMap. All rights reserved.