69

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

Amal K
  • 4,359
  • 2
  • 22
  • 44
Erre Efe
  • 15,387
  • 10
  • 45
  • 77
  • there are a lot of image formats. – Daniel A. White Jun 16 '12 at 13:39
  • 1
    @DanielA.White That's precisely the point of my question. Determine that it's any image format but an image after all. – Erre Efe Jun 16 '12 at 13:41
  • 1
    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 http://stackoverflow.com/questions/210650/validate-image-from-file-in-c-sharp – moribvndvs Jun 16 '12 at 13:42
  • DO THIS PROPERLY and [use Binary Analysis for Mime Type detection](https://stackoverflow.com/questions/15300567/alternative-to-findmimefromdata-method-in-urlmon-dll-one-which-has-more-mime-typ/15595571). – Jeremy Thompson Feb 14 '19 at 04:50
  • Check this: http://www.codingfusion.com/Post/Check-if-an-uploaded-file-Is-an-Image-of-any-format-in-NET-MVC – MaxPayne Aug 08 '23 at 03:26

12 Answers12

125

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.

Amal K
  • 4,359
  • 2
  • 22
  • 44
OzB
  • 2,140
  • 1
  • 22
  • 38
  • What's your idea for these file types: `".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pps", ".ppsx"`, please. – Rasool Ghafari Dec 05 '13 at 14:43
  • Besides from validating the file extension, you can validate the first bytes as well (see for example http://serverfault.com/questions/338087/making-libmagic-file-detect-docx-files/377792#377792). 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. – OzB Dec 08 '13 at 16:06
  • 3
    Can you modify this to allow for ASP.NET Core as there is no drawing or bitmap class? Thanks a lot – Techy Feb 19 '17 at 20:43
  • 1
    `if (postedFile.ContentLength > ImageMinimumBytes)` This is a typo no? It should read `if (ContentLength < ImageMinimumBytes)` – TomSelleck Jul 06 '18 at 15:11
  • I have corrected the bug/typo mentioned by TomSelleck in my adaption of this answer for 2018 using .NET CORE 2.1 (see below). – René Aug 16 '18 at 15:04
  • You're absolutely right, @TomSelleck . Fixed here as well. – OzB Jan 05 '19 at 11:39
  • @OzB Thanks for posting, proved very useful when I needed it! – TomSelleck Jan 05 '19 at 21:31
  • @OzB: Compare what "exceeds the size limit" means to what your code does now. – OwenP Sep 23 '20 at 17:35
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;
    }
}
René
  • 3,413
  • 2
  • 20
  • 34
  • 4
    I'm using .net core 3.1 and I had to add reference the System.Drawing.Common library for this to work – Max Carroll Apr 03 '20 at 15:20
  • 2
    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, – Max Carroll Apr 07 '20 at 12:05
  • 1
    @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. – René Apr 07 '20 at 13:07
  • I'd suggest that this would be a lot better if it returned true for each condition - unless you want to check everything? – RemarkLima Feb 17 '21 at 14:06
  • 1
    same as @MaxCarroll, this breaks on Fargate. The issue is in System.Drawing: https://github.com/dotnet/runtime/issues/21980 – GAltelino Apr 07 '21 at 16:55
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.

Shawn Gavett
  • 458
  • 1
  • 5
  • 12
  • This also perfectly valid in my case (well, IFormFile does not contain ContentLength but Length property) – Juan Dec 08 '19 at 08:32
  • This should be the accpeted answer – Boban Stojanovski Apr 22 '20 at 22:40
  • If uploading a file, check the headers of the MultipartFileData ```if (file.Headers.ContentLength > 0 && file.Headers.Contains("image"))``` – Boban Stojanovski Apr 22 '20 at 22:44
  • this code only validates file name. it does NOT validate the file is an image. – Edward Olamisan May 02 '20 at 01:45
  • 1
    @EdwardOlamisan, are you talking about the answer or the comment above your comment? My code reviews the contentType which is not the filename – Shawn Gavett May 04 '20 at 16:51
  • 1
    @ShawnGavett sorry, my mistake, I copy-pasted my comment to another answer because they are so similar: https://stackoverflow.com/a/41395863/556649. 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. – Edward Olamisan May 04 '20 at 18:45
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
}
Wiktor Zychla
  • 47,367
  • 6
  • 74
  • 106
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.

TheZodchiy
  • 136
  • 2
  • 11
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());
    }
}
Project Mayhem
  • 439
  • 5
  • 12
Arun Selva Kumar
  • 2,593
  • 3
  • 19
  • 30
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.

CervEd
  • 3,306
  • 28
  • 25
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;
    }
Amal K
  • 4,359
  • 2
  • 22
  • 44
Kishan Patel
  • 161
  • 1
  • 8
0

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

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
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
    }
hitasp
  • 698
  • 2
  • 8
  • 19
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;
    }
  • 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](/help/how-to-answer). – Community Feb 13 '23 at 01:23
-2

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

DropAndTrap
  • 1,538
  • 16
  • 24