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 |
---|---|
jpeg, jpg, gif, png | |
any | |
| |
doc, docx, epub, html, htm, mht, mhtml, odt, txt, rtf, xml | |
xlsx, xlsm, xls, xltx, xltm, xlt, txt, csv | |
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:
- Specify exact content type when adding binary data to the response.
Add the
X-CONTENT-TYPE-OPTIONS="nosniff"
header to the response.Response.ContentType = "image/jpeg"; Response.Headers.Add("X-Content-Type-Options", "nosniff");