Skip to main content

Validate Uploaded Files

  • 4 minutes to read

To prevent a threat actor from uploading/executing files with malicious code (CWE-434), you should validate uploaded files and always specify accepted/valid file types.

Validate File Extensions

DevExpress ASP.NET MVC extensions allow users to upload the following file types (file extensions) by default:

Extension

Allowed File Extensions

Binary Image

jpeg, jpg, gif, png

File Manager

any

Html Editor

  • Audio Dialogs - mp3, ogg
  • Image Dialogs - jpe, jpeg, jpg, gif, png
  • Video Dialogs - mp4, ogg
  • Flash Dialogs - swf

Rich Text Editor

doc, docx, epub, html, htm, mht, mhtml, odt, txt, rtf, xml

Spreadsheet

xlsx, xlsm, xls, xltx, xltm, xlt, txt, csv

Upload Control

any

If you do not restrict acceptable/valid file extensions for the DevExpress File Manager and Upload Control, a threat actor may be able to upload a malicious file that can be executed on the server. If the threat actor requests this file by URL, your application will execute the malicious code as if it was a part of your application.

Use the AllowedFileExtensions property to set acceptable/valid file extensions. This causes validation to fail and the control to display an error message if a file extension has not been specified. The NotAllowedFileExtensionErrorText property allows you to customize error text.

The following code snippet limits file upload operations (when using the DevExpress File Manager extension) to JPG and PNG files:

@Html.DevExpress().FileManager(settings => {
    settings.Name = "fileManager";
    settings.DownloadRouteValues = new { Controller = "FileManager", Action = "DownloadFiles" };
    settings.CallbackRouteValues = new { Controller = "FileManager", Action = "FileManagerPartial" };
    settings.Settings.AllowedFileExtensions = new[] { ".jpg", ".png" };
    // ...
}).BindToFolder(Model).GetHtml()

For the Upload Control extension, you should limit allowed file extensions in both view and controller code. To do the latter, implement a custom model binder that initializes the AllowedFileExtensions setting with a list of allowed file extensions. Specify the FilesUploadCompleteHandler to perform any additional checks.

@using(Html.BeginForm()) {
    @Html.DevExpress().UploadControl(settings => {
        settings.Name = "uploadControl";
        settings.UploadMode = UploadControlUploadMode.Auto;
        settings.CallbackRouteValues = new {Controller = "UploadFiles", Action = "UploadFilesHandler"};
        settings.ShowUploadButton = true;
        settings.ValidationSettings.AllowedFileExtensions = new[] { ".jpg", ".png" };
    }).GetHtml()
}
public class UploadFilesController : Controller {
    public ActionResult UploadFilesHandler([ModelBinder(typeof(UploadFilesBinder))] IEnumerable<UploadedFile> uploadControl) {
        return null;
    }
    public class UploadFilesBinder : DevExpressEditorsBinder {
        public UploadFilesBinder() {
            UploadControlBinderSettings.ValidationSettings.AllowedFileExtensions = new[] { ".jpg", ".png" };
            UploadControlBinderSettings.FilesUploadCompleteHandler = uploadControl_FilesUploadComplete;
        }
        private void uploadControl_FilesUploadComplete(object sender, FilesUploadCompleteEventArgs e) {
            var uploadedFiles = ((MVCxUploadControl)sender).UploadedFiles;
            if (uploadedFiles != null && uploadedFiles.Length > 0) {
                for (int i = 0; i < uploadedFiles.Length; i++) {
                    UploadedFile file = uploadedFiles[i];
                    if (file.IsValid && file.FileName != "") {
                        using (var stream = file.FileContent) {
                            // Validate and save the file here
                        }
                    }
                }
            }
        }
    }
}

Validate File Content

You should always validate uploaded files before saving files to a server. If you do not validate uploaded files, a threat actor can upload a file with a malicious script using a valid file extension (CWE-79).

The Binary Image extension automatically checks whether an uploaded file is an image. In the following example, the DevExpress Upload Control checks whether the uploaded file is a valid image:

@using(Html.BeginForm()) {
    @Html.DevExpress().UploadControl(settings => {
        settings.Name = "uploadControl";
        settings.UploadMode = UploadControlUploadMode.Auto;
        settings.CallbackRouteValues = new {Controller = "UploadFile", Action = "UploadFileHandler"};
        settings.ShowUploadButton = true;
        settings.ValidationSettings.AllowedFileExtensions = new[] { ".jpg", ".png" };
    }).GetHtml()
}
public class UploadFilesController : Controller {
    public ActionResult UploadFileHandler([ModelBinder(typeof(UploadFileBinder))] IEnumerable<UploadedFile> uploadControl) {
        return null;
    }
    public class UploadFileBinder : DevExpressEditorsBinder {
        public UploadFileBinder() {
            UploadControlBinderSettings.ValidationSettings.AllowedFileExtensions = new[] { ".jpg", ".png" };
            UploadControlBinderSettings.FileUploadCompleteHandler = uploadControl_FileUploadComplete;
        }
        private void uploadControl_FileUploadComplete(object sender, FileUploadCompleteEventArgs e) {
            if(!e.UploadedFile.IsValid) return;

            using(var stream = e.UploadedFile.FileContent) {
                if (!IsValidImage(stream)) {
                    e.ErrorText = "Invalid image.";
                    e.IsValid = false;
                }
                else {
                    string fileName = System.Web.HttpContext.Current.Request.MapPath("~/App_Data/UploadFiles/avatar.jpg");
                    e.UploadedFile.SaveAs(fileName, true);
                }
            }
        }
        static bool IsValidImage(Stream stream) {
            try {
                using (var image = System.Drawing.Image.FromStream(stream)) {
                    return true;
                }
            }
            catch (Exception) {
                return false;
            }
        }
    }
}

Use third-party antivirus tools to validate uploaded files where possible.

Disable Uploaded File Execution

To protect your application, restrict the execution of files located in the upload folder. The following code snippets restricts file execution for the UploadedFiles/Images folder:

<location path="UploadedFiles/Images">
    <system.webServer>
        <handlers>
            <clear />
            <add name="StaticFile"
                 path="*"
                 verb="*"
                 modules="StaticFileModule"
                 resourceType="Either"
                 requireAccess="Read" />
      </handlers>
    </system.webServer>
</location>

Validate Response Content Type

Web browsers use MIME-type sniffing techniques to identify file type based on file content. Threat actors can exploit this technique and create malicious files that have valid extensions, but browsers interpret these files as executable. Follow the steps below to prevent browsers from content sniffing:

  1. Specify exact content type when adding binary data to the response.
  2. Add the X-CONTENT-TYPE-OPTIONS="nosniff" header to the response.

    Response.ContentType = "image/jpeg";
    Response.Headers.Add("X-Content-Type-Options", "nosniff");
    
See Also