Web Application Security Basics — Safe file upload and File I/O

Kasun Balasooriya
7 min readOct 5, 2015

--

File upload and file i/o is a common feature which most of the web applications allow. It’s a critical part of any web application which should be carefully crafted and implemented since a single mistake could cause lot of trouble to an application.

Through this post I am going address some of the design flaws , attack patterns and file upload security.

Design flaw 1 : File Path Injection

Path injection otherwise known as path traversal is a critical design flaw which can lead to a lot of vulnerabilities. The goal of the path traversal attack is to gain access to resources that should otherwise not be accessible. The classic definition involves reading resources that are outside of the web application folder, but as it can actually be equally dangerous to read or write files inside or outside of the web application folder.

The key indicator of a file path injection vulnerability is an application that accepts ../ as part of a URL or file name parameter.

Example:

suppose you have a servlet that lets users download a file. It takes a filename as a parameter and then streams the file to the user like so:

http://www.awesomefileupload.com/downloadFile?filename= path/to/my_vacation_photos.zip

If an attacker chooses to substitute the following instead of the above the attacker can gain access to the etc/passwd file.

http://www.awesomefileupload.com/downloadFile?filename= path/to/../../../../../etc/passwd

If your web server is running as the root user, there is almost no limit to what data could be ex filtrated from your server. The /etc/shadow file containing your server’s password hashes, a context.xml file containing credentials to your database, or the Java keystore file with your SSL private key are some of the examples which a attacker might target.

The key to defending against file path injection is to first properly canonicalize, and then validate, any given file path:

Example:

The key to defending against file path injection is to first properly
canonicalize, and then validate, any given file path:
File file = new File(input);
String canonicalPath = file.getCanonicalPath(); //canonicalize
//validate
if (!canonicalPath.startsWith(“/my/allowed/path”)) {
//reject the input
}

Design flaw 2: Null byte injection

Null byte injection occurs when untrusted data somehow taints a file I/O operation, allowing the attacker to bypass input validation of a filename. Many Java operations are actually wrappers around native C++ functions. C++ interprets the null byte as a control character that means “stop processing this string.” So, when out-of-date versions of the Java Runtime Environment receive a null byte (%00) as part of a file path operation, validation can fail:

// Assume
request.getParameter(‘filename’).equals(“../etc/passwd%00.jpg”)
String xmlFileName = request.getParameter(“filename”);
if (fileName.endsWith(“.xml”)) {
File xmlFile = new File(fileName);
//stream file to user
streamFile(xmlFile);
}

If you are using a version of Java that is vulnerable to null byte injection, the following attack could bypass the preceding code:
http://www.site.com/files?filename=../../../../etc/passwd%00profile.jpg

This vulnerability was fixed in Java SE 7 Update 40 and Java SE 8 family by explicitly checking for the presence of a null byte in many file operations.

Although the need to protect against null byte injection no longer exists in updated JVMs, detecting and defending against null bytes in input is still an important aspect of a strong defense-in-depth.

Design flaw 3: Not closing resources properly

It is important to properly close file handles to prevent a denial of service (DoS). This can be explained using the following example.

The problem is that file I/O operations can throw lots of exceptions. If an exception had occurred anywhere in that method it would have short-circuited the calls to close the streams.

In order to fix this issue, you need to make sure that you close any open resources in a finally block to ensure that those calls get executed regardless of the outcome of the try.

Fortunately, the Java powers-that-be recognized that this construct was clumsy at best and in Java 7 introduced the try-with-resources statement. Now you can declare resources that need to be closed at the end of the try block and they will be closed for you automatically, regardless of the outcome:

try (
InputStream fileIn = getUploadInputStream(); //the resources
OutputStream fileOut = new
FileOutputStream(validatedFilePath);
)
{
byte[] buffer = new byte[bufferSize];
int byteCount = 0;
08-ch08.indd 206 22/07/14 3:02 pm
Chapter 8: Safe File Upload and File I/O 207
ORACLE FL / Iron-Clad Java: Building Secure Web Applications / Manico & Detlefsen / 588–1 / Chapter 8
while ((byteCount = input.read(buffer)) >= 0) {
output.write(buffer, 0, byteCount);
}
} catch (IOException ioe) {
//handle the exception somehow
}

File Upload Security

Building a secure file upload mechanism requires several layers of defense. The application code, the database, and the filesystem all need various performance, storage, and security design considerations. You need to validate that the filename is safe, use object reference maps to save the file safely on your web server, verify that the content of the file upload is safe, and manage quota.

Attacks patterns against file I/O

Attack 1: Upload of dangerous content

The following are just some types of dangerous files that could be uploaded:

■ Viruses and other malware that could infect your users.
■ HTML files containing XSS or CSRF attacks.
■ Executable code, such as a JSP file or WAR file.
■ Even a PHP or ASP file could be dangerous in environments where
multiple technologies are in use.

Validating file extensions

Any JavaScript contained in these files will run in the same domain as the site itself, completely circumventing the browser’s Same Origin Policy, giving an attacker a perfect vector for stored XSS. Likewise, CSS in these files can override your own site’s styles and be a means for defacement of your site.

If for some reason you absolutely have to support dangerous file types such as .jsp or .war (for example, if you are creating a developer portal) they should be stored on a completely separate domain and served by a separate web server that is not a Java app server.

Virus and malware detection

One of the more challenging aspects of a file upload mechanism is ensuring that the contents of the file are not malicious. When files are uploaded, they are immediately placed into a quarantine directory where they are checked for malware. Once the antivirus check succeeds, only then should you make the file available for your other users to download.

Verifying file contents

Another important step is to verify that the contents of a file actually match the stated file type. For example, you can verify that an uploaded file with the extension .jpg is actually a valid image by the technique of image rewriting.

Attack 2: Ability to Overwrite Other Files

File upload functionality can also sometimes be abused with file path injection to write files as well. The content of the website can be tampered by an attacker using this vulnerability if it prevails in your website.

The way to prevent this is to properly canonicalize and validate all filename input to your file upload application. The OWASP Java File I/O Security Project contains functions to validate whether a filename is valid or not.

In addition, it is prudent to follow the Principle of Least Privilege and ensure that any files that are actually part of your application are not writable by the web server itself.

Attack 3: Quota Overload DoS

When allowing your users to upload files, setting a per-user upload quota is necessary to ensure proper management of your storage. If no limit is imposed, a malicious user could upload many terabytes of files and consume all storage on your device, resulting in a denial of service.

There are several things to consider when building a quota policy.

  • set a maximum total storage quota for each user
  • limit the size of any single file to prevent an attacker from abusing file verification mechanisms by passing in files that will consume an inordinate amount of RAM or processor cycles to validate.
  • limit the total number of files a user can upload to avoid slowing down the filesystem on your server (and how many database rows they can create).

It is important to set not only a maximum file size, but also the maximum size of an entire request. Consider what would happen if an adversary determined that your maximum file size was 1MB, but then proceeded to send you several concurrent requests, each with thousands of 1MB minus 1 byte files. Your application would parse each file in the request, rapidly filling up RAM or disk space and possibly crashing your server even before the entire request finishes parsing.

Processing Archive formats

Most zip or other archive APIs allow you to inspect the total uncompressed size before conducting the actual uncompress action. In java. util.zip.ZipFile, this is accomplished by iterating through each ZipEntry and adding the results of the getSize() method:

ZipFile zipFile = new ZipFile(“testzip.zip”);
long totalUncompressedSize = 0;
Enumeration<? extends ZipEntry> e = zipFile.entries();
while (e.hasMoreElements()) {
ZipEntry ze = e.nextElement();
long uncompressedSize = ze.getSize();
totalUncompressedSize += uncompressedSize;
}

Positive Pattern: Object Reference Maps and Storing Upload Files

Another useful precaution is to not use the actual filename provided by users to save the uploaded file. Instead, use a machine generated name that is stored in your database along with the original filename. When you create a link for a user to download a file, you should only reveal the database key for the file.
Then, after checking whether the user is authenticated and authorized for the file, you can set the original filename into the response headers when you stream the file out to them.In this way, the filename and path are never revealed to the user at all, and there is no opportunity for path manipulation.

More info on direct object references can be found at :

A brief overview of OWASP top 10 risks and what it takes to minimize them in java.(Part -3)

Originally published at http://neatrick.wordpress.com on October 5, 2015.

--

--

Kasun Balasooriya
Kasun Balasooriya

No responses yet