You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Next »

Check inputs for cases that cause consumption of excessive system resources. Denial of service can occur when resource usage is disproportionately large in comparison to the input data that causes the resource usage.

This guideline is of greater concern for persistent, server-type systems than for desktop applications. Checking inputs for excessive resource consumption may be unjustified for client software that expects the user to handle resource-related problems. Even for client software, however, should check for inputs that could cause persistent denial of service, such as filling up the file system.

SCG 2009 lists some examples of possible attacks:

  • Requesting a large image size for vector graphics, for instance, SVG and font files.
  • "Zip bombs" whereby a short file is very highly compressed, for instance, ZIPs, GIFs and gzip encoded http contents.
  • "Billion laughs attack" whereby XML entity expansion causes an XML document to grow dramatically during parsing. Set the XMLConstants.FEATURE_SECURE_PROCESSING feature to enforce reasonable limits.
  • Using excessive disc space.
  • Inserting many keys with the same hash code into a hash table, consequently triggering worst-case performance (O(n 2)) rather than typical-case performance (O(n)).
  • Initiating many connections where the server allocates significant resources for each, for instance, the traditional "SYN flood" attack.

Noncompliant Code Example

This noncompliant code fails to check the resource consumption of the file that is being unzipped. It permits the operation to run to completion or until local resources are exhausted.

// external data source: args[0]
const int BUFFER = 512;
BufferedOutputStream dest = null;
FileInputStream fis = new FileInputStream(args[0]);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));
ZipEntry entry;
while((entry = zis.getNextEntry()) != null) {
  System.out.println("Extracting: " +entry);
  int count;
  byte data[] = new byte[BUFFER];
  // write the files to the disk
  FileOutputStream fos = new FileOutputStream(entry.getName());
  dest = new BufferedOutputStream(fos, BUFFER);
  while ((count = zis.read(data, 0, BUFFER)) != -1) {
    dest.write(data, 0, count);
  }
  dest.flush();
  dest.close();
}
zis.close();

Implementation Details

The Zip algorithm is capable of producing very large compression ratios [Mahmoud 2002]. The example below shows a file that was compressed from 148MB to 590KB, a ratio of more than 200 to 1. The file consists of arbitrarily repeated data: alternating lines of 'a' characters and 'b' characters. Even higher compression ratios can be easily obtained using more input data, more targeted input data, and other compression methods.

Compliant Solution

In this compliant solution, the code inside the while loop uses the ZipEntry.getSize() to find the uncompressed filesize of each entry in a zip archive before extracting the entry. It throws an exception if the entry to be extracted is too large — 100MB in this case.

  // write the files to the disk - if file is not insanely big
  const int TOOBIG = 0x6400000; // 100MB
  if (entry.getSize() > TOOBIG) 
    throw new RuntimeException("File to be unzipped is huge.");
  FileOutputStream fos = new FileOutputStream(entry.getName());
  dest = new BufferedOutputStream(fos, BUFFER);
  while ((count = zis.read(data, 0, BUFFER)) != -1) {
    dest.write(data, 0, count);
  }

Risk Assessment

Guideline

Severity

Likelihood

Remediation Cost

Priority

Level

IDS18-J

low

probable

high

P2

L3

Bibliography

[SCG 2009] Secure Coding Guidelines for the Java Programming Language, version 3.0
[Mahmoud 2002] Compressing and Decompressing Data Using Java APIs


IDS17-J. Understand how escape characters are interpreted when String literals are compiled      13. Input Validation and Data Sanitization (IDS)      14. Platform Security (SEC)

  • No labels