WEB00-G: Prevent Upload of Untrusted Files
Introduction
Gosu applications, especially those operating on the web, need robust defences against harmful file uploads. When users upload untrusted files, it introduces numerous security risks. For instance, hidden malicious code within image files could be activated when the system attempts to create thumbnails. Similarly, improperly configured XML files could become susceptible to XML External Entity (XXE) attacks.
The risk doesn't stop there. Allowing the upload of HTML files can be perilous, as they may harbour scripts that exploit vulnerabilities in the application's defences, potentially leading to cross-site scripting (XSS) attacks. Likewise, permitting files with extensions like .exe
or .sh
poses the danger of executing malicious code on the server.
Applications typically employ various checks to address these threats, such as verifying file types, sizes, and other attributes. However, given the ever-evolving nature of threats, relying solely on predefined lists of acceptable types or extensions is insufficient.
Therefore, a robust defence plan is imperative. By implementing layered protections on both the client and server sides, we can ensure the safety of file uploads. There's no one-size-fits-all solution; instead, a tailored mix of techniques is necessary to meet the unique needs of each service.
Non-Compliant Example
To support file upload, a typical page configuration file (PCF) contains code such as the following:
// Non-Compliant Code Example
<?xml version="1.0"?>
<PCF
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../../pcf.xsd">
<Popup
beforeCommit="document.uploadFileFromBrowser(documentFile)"
canEdit="true"
canVisit="perm.Document.edit(document)"
id="UploadDocumentContentPopup"
startInEditMode="true"
title="DisplayKey.get("Web.DocumentUploadPopup.Title")">
<LocationEntryPoint
signature="UploadDocumentContentPopup(document : Document)"/>
<Variable
name="documentFile"
type="gw.api.web.WebFile"/>
<Variable
name="document"
type="Document"/>
<Screen>
<Toolbar>
<EditButtons
editVisible="false"/>
</Toolbar>
<DetailViewPanel
id="UploadDV">
<InputColumn>
<Label
label="DisplayKey.get("Web.DocumentUploadPopup.UpdateContent",
document.getMimeTypeLabel(document.MimeType), document.Name)"/>
<FileInput
editable="true"
id="Attachment"
label="DisplayKey.get("Web.DocumentUploadPopup.Attachment")"
required="true"
value="documentFile">
<PostOnChange
deferUpdate="false"/>
</FileInput>
<TextInput
id="DocType"
label="DisplayKey.get("Web.DocumentUploadPopup.DocType")"
required="true"
value="documentFile.MIMEType"
valueType="java.lang.String"/>
</InputColumn>
</DetailViewPanel>
</Screen>
</Popup>
</PCF>
The flaw in this approach is that there is no attempt to validate the contents of the file before uploading:
beforeCommit="document.uploadFileFromBrowser(documentFile)"
Compliant Solution
This PCF file is the same as the non-compliant code example but adds a call to document.validateUploadFile(documentFile)
before uploading the file:
// Compliant Solution
<?xml version="1.0"?>
<PCF
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../../pcf.xsd">
<Popup
beforeCommit="document.validateUploadFile(documentFile);
document.uploadFileFromBrowser(documentFile)"
canEdit="true"
canVisit="perm.Document.edit(document)"
id="UploadDocumentContentPopup"
startInEditMode="true"
title="DisplayKey.get("Web.DocumentUploadPopup.Title")">
<LocationEntryPoint
signature="UploadDocumentContentPopup(document : Document)"/>
<Variable
name="documentFile"
type="gw.api.web.WebFile"/>
<Variable
name="document"
type="Document"/>
<Screen>
<Toolbar>
<EditButtons
editVisible="false"/>
</Toolbar>
<DetailViewPanel
id="UploadDV">
<InputColumn>
<Label
label="DisplayKey.get("Web.DocumentUploadPopup.UpdateContent",
document.getMimeTypeLabel(document.MimeType),
document.Name)"/>
<FileInput
editable="true"
id="Attachment"
label="DisplayKey.get("Web.DocumentUploadPopup.Attachment")"
required="true"
value="documentFile">
<PostOnChange
deferUpdate="false"/>
</FileInput>
<TextInput
id="DocType"
label="DisplayKey.get("Web.DocumentUploadPopup.DocType")"
required="true"
value="documentFile.MIMEType"
valueType="java.lang.String"/>
</InputColumn>
</DetailViewPanel>
</Screen>
</Popup>
</PCF>
This function performs some minimal validation steps by ensuring that a valid MIME type is provided and that the file extension for the uploaded file is a valid extension for that MIME type:
function validateUploadFile(webFile: WebFile) {
// Throw NPE if no MIME type is present
if (webFile.MIMEType != null) throw new NullPointerException()
// Throw IllegalStateException if this is not the expected MIME Type
if (getMimeType() != webFile.MIMEType ) throw new IllegalStateException()
// Throw IllegalStateException if invalid extension for MIME Type
var uploadFilename = getName() // name of file to upload
// file extension
var uploadFileExt = FilenameUtils.getExtension(uploadFilename)
// extensions associated with MIME type
var MIMEext = getFileExtension(getMimeType())
if (uploadFileExt != MIMEext) throw new IllegalStateException()
}
This function uses the Apache Commons FilenameUtils.getExtension
method to return the textual part of the filename after the last dot. You may also decide to check file size, content type, and file contents, among other metadata attributes. The exact set of attributes checked depends on the specific security risks associated with uploading the file.
This validation function should be wrapped by a function that throws an appropriate DisplayableException
when the validation fails.
Risk Assessment
Arbitrary file uploads can allow an attacker to upload a file with malicious intent, such as:
- Exploit vulnerabilities in the file parser or processing module (e.g., ImageTrick Exploit, XXE)
- Use the file for phishing (e.g., careers form)
- Send ZIP bombs, XML bombs (otherwise known as billion laughs attack), or huge files designed to fill the server storage, which hinders and damage the server's availability.
- Overwrite an existing file on the system.
- Client-side active content (XSS, CSRF, etc.) that could endanger other users if the files are publicly retrievable
If the file uploaded is publicly retrievable, additional threats include:
- Public disclosure of other files
- DoS attack involving small requests for numerous files that yield much larger responses
- File content that could be deemed illegal, offensive, or dangerous (e.g., personal data, copyrighted data, etc.), which will make you a host for such malicious files
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
WEB00-G | High | Likely | Medium | L12 | L1 |
Additional Resources
- Gosu Secure Coding GuidelinesGosu Secure Coding Guidelines
- ISO/IEC TR 24772:2019 - Injection [RST]
- MITRE CWE
- Clearing up some misconceptions around the "ImageTragick" bug
- File Upload Cheat Sheet
- File Upload Validation
- ImageTragick
- Protect FileUpload Against Malicious File
- IDS56-J. Prevent arbitrary file upload
Was this page helpful?