CONNECT WITH US

Featured Post

Validate uploaded image content in ASP.NET

Get article update by Email
A very few day ago i had to face the situation where hacker uploaded the malicious ASP script to the web server by changing its extension to .JPG through user interface which allow user to upload image file. Although developer team had put the validation on extension. But unfortunately extension of ASP script was .JPG and hence it is allowed extension. So hacker could upload that malicious script.

After this situation i thought just checking only extension for uploaded file is not the sufficient. We need to check content as well of the uploaded file.

So i decided to check header information in uploaded image file. If it found valid header information then only save uploaded file otherwise discard uploaded file. After Digging couple of hour on http://www.wotsit.org(I am fan of www.wotsit.org for more than 5 years), found following Header Information about different image file format.

Image File Header Information Table
File Format
Offset
Length
Value
JPG / JPEG
0
4
0xFF, 0xD8, 0xFF, 0xE0
PNG
0
4
0x89, 0x50, 0x4E, 0x47
TIF / TIFF
0
4
0x49, 0x49, 0x2A, 0x00
GIF
0
4
0x47, 0x49, 0x46, 0x38
BMP
0
2
0x42, 0x4D
ICO
0
4
0x00, 0x00, 0x01, 0x00

Instead of checking only header, we could also check whole file content against its file format. But checking only header could serve our purpose and it is also speedy process than checking whole file content so i am not checking whole file content.

Following code snippet validate header of known image types (JPG, PNG, TIFF, GIF, BMP, ICO) Please do let me know if i have missed any image types.

NOTE: In code snippet fuImage refer to ASP.NET file upload control

protected void btnUpload_Click(object sender, EventArgs e)
{
    // DICTIONARY OF ALL IMAGE FILE HEADER
    Dictionary<string, byte[]> imageHeader = new Dictionary<string, byte[]>();
    imageHeader.Add("JPG", new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 });
    imageHeader.Add("JPEG", new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 });
    imageHeader.Add("PNG", new byte[] { 0x89, 0x50, 0x4E, 0x47 });
    imageHeader.Add("TIF", new byte[] { 0x49, 0x49, 0x2A, 0x00 });
    imageHeader.Add("TIFF", new byte[] { 0x49, 0x49, 0x2A, 0x00 });
    imageHeader.Add("GIF", new byte[] { 0x47, 0x49, 0x46, 0x38 });
    imageHeader.Add("BMP", new byte[] { 0x42, 0x4D });
    imageHeader.Add("ICO", new byte[] { 0x00, 0x00, 0x01, 0x00 });
 
    byte[] header;
    if (fuImage.HasFile)
    {
        // GET FILE EXTENSION
        string fileExt;
        fileExt = fuImage.FileName.Substring(fuImage.FileName.LastIndexOf('.') + 1).ToUpper();
 
        // CUSTOM VALIDATION GOES HERE BASED ON FILE EXTENSION IF ANY
        
        byte[] tmp = imageHeader[fileExt];
        header = new byte[tmp.Length];
 
        // GET HEADER INFORMATION OF UPLOADED FILE
        fuImage.FileContent.Read(header, 0, header.Length);
 
        if (CompareArray(tmp, header))
        {
            lblMessage.Text = "Valid ." + fileExt + " file.";
            // VALID HEADER INFORMATION 
            // CODE TO PROCESS FILE
        }
        else
        {
            lblMessage.Text = "Invalid ." + fileExt + " file.";
            // INVALID HEADER INFORMATION
        }
    }
    else
    {
        lblMessage.Text = "Please select image file.";
    }
}
 
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;
}
Any input on above is greatly appreciated...


Subscribe via RSS
Like us on Facebook
Follow us on Twitter
Subscribe via Email

5 comments:

Mr Baldman said...

why not just load the byte array into a system.drawing.image object?

Anonymous said...

And why not using "System.IO.Path.GetExtension(fileName)" to retrieve the extension?

Anonymous said...

Why not just check the Mime Type?
Isn't that better?

terse said...

Excellent article. Same thing happened to one of my client's web site. Though it is written in PHP, the concept and content of your article will definitely help me put an end to this. Currently it has become an issue because hackers get access to the system as a legitimate user by exploiting a hacked email address and later using forgot password feature of our web sites.

Thanks

-Fazle

Anonymous said...

Use

Private Shared Function IsValidImageFile(ByVal file As System.Web.UI.WebControls.FileUpload) As Boolean
Try
Using image As New Bitmap(file.PostedFile.InputStream)
Return True
End Using
'throws exception if not valid image
Catch generatedExceptionName As ArgumentException
End Try

Return False
End Function


Is simpler! ;-)

Post a Comment