Secure coding: Prevent unauthorized access through path traversal (CWE-22)

CWE-22 describes the improper modification of a path name to a restricted directory. How can the vulnerability be addressed?

Save to Pocket listen Print view
Secure coding: Prevent unauthorized access through path traversal (CWE-22)

(Image: erstellt mit ChatGPT / DALL-E)

9 min. read
By
  • Sven Ruppert
Contents

Common Weakness Enumeration CWE-22, commonly referred to as "path traversal", is a vulnerability in which an application does not appropriately restrict the paths that users can access via user-supplied input. This can allow attackers to access directories and files outside of the intended directory, leading to unauthorized access and possible system compromise. This vulnerability is particularly significant in Java applications due to the ubiquitous use of file processing and web resources. This article discusses the nature of CWE-22, its impact, exploitation methods and, most importantly, mitigation strategies for such vulnerabilities in Java applications.

CWE-22 is categorized as "Improper restriction of a pathname to a restricted directory (path traversal)". The vulnerability occurs when an application uses user input to create a path without proper validation or sanitization, allowing someone to specify paths that go outside the intended directory. Sequences such as ../ can be used to move up one level in the directory structure.

For example, a typical Java code snippet that reads a file based on user input looks like this:

String fileName = request.getParameter("file");
File file = new File("/var/www/uploads/" + fileName);
FileInputStream fis = new FileInputStream(file);

If the fileName parameter is not properly validated, an attacker can manipulate it to access files outside the /var/www/uploads/ directory, such as /etc/passwd, by using a path traversal sequence (`. ./../etc/passwd`).

The impact of CWE-22 can be severe, ranging from unauthorized access to sensitive files to complete system compromise. Potential impacts include:

  • Exposure of sensitive information: Attackers can access sensitive files such as configuration files, passwords and personal information.
  • Data integrity compromise: Attackers can modify files, potentially leading to data corruption or modification of critical application files.
  • Denial of Service (DoS): Attackers can disrupt normal operations by accessing and modifying system files.
  • Execution of arbitrary code: In extreme cases, attackers can execute arbitrary code if they gain access to executable files or scripts.

Exploitation techniques

To exploit a path traversal vulnerability, an attacker typically uses special characters and patterns in the input to navigate through the file system. Some of the standard techniques are:

  • Dot-dot-slash (../): The most common method where the attacker uses ../ to move up the directory structure.
  • Encoded characters: Attackers may use URL encoding (%2e%2e%2f) or other encoding schemes to bypass simple input filters.
  • Null byte injection: Sometimes null bytes (%00) are used to terminate strings prematurely, effectively ignoring any appended extensions or path components.

Mitigation strategies in Java

Mitigating the potential dangers of CWE-22 in Java requires a combination of safe coding practices, input validation, and proper use of APIs. Recommended strategies include:

Canonicalization and normalization

Ensure that file paths are normalized before use. Java provides methods for normalizing paths that can help mitigate traversal attacks.

  import java.nio.file.Paths;
   import java.nio.file.Path;

   public File getFile(String fileName) throws IOException {
       Path basePath = Paths.get("/var/www/uploads/");
       Path filePath = basePath.resolve(fileName).normalize();

       if (!filePath.startsWith(basePath)) {
           throw new SecurityException("Attempted path traversal attack detected");
       }

       return filePath.toFile();
   }

Input validation and sanitization

Perform strict validation of user input. Reject any input that contains potentially malicious patterns such as ../, URL-encoded sequences or other traversal patterns.

 public String sanitizeFileName(String fileName) {
       if (fileName == null 
             || fileName.contains("..") 
             || fileName.contains("/") 
             || fileName.contains("\\")) {
           throw new IllegalArgumentException("Invalid file name");
       }
       return fileName;
   }

Whitelist approach

Use a whitelist to validate filenames against a set of allowed values or patterns. This can be more effective than blacklisting known destructive patterns.

  public boolean isValidFileName(String fileName) {
       return fileName.matches("[a-zA-Z0-9._-]+");
   }

Security manager and permissions

Use Java's security manager to restrict access to the file system. This adds an additional layer of protection by enforcing access control policies at runtime. However, the respective Java version must be taken into account here, as the SecurityManager is to be dropped in one of the future JDK versions.

   System.setSecurityManager(new SecurityManager());

File access controls

Restrict file permissions on the server to limit access to only the necessary files and directories. This reduces the impact of potential exploits. The web service process must run under a defined user whose rights are restricted to what is absolutely necessary.

Logging and monitoring

Implement comprehensive logging and monitoring to detect and respond to suspicious activity. Logs should capture enough detail to track potential exploit attempts.

 import java.util.logging.Logger;

   public class FileAccessLogger {
       private static final Logger LOGGER = 
                    Logger.getLogger(
                        FileAccessLogger.class.getName());

       public void logAccessAttempt(String fileName) {
           LOGGER.warning("Attempted access to file: " + fileName);
       }
   }

Let's look at a hypothetical case study to illustrate the application of the above remediation strategies. Suppose we have a simple Java web application that allows users to download files from a server.

@WebServlet("/download")
public class FileDownloadServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
                                  throws ServletException, IOException {
        String fileName = request.getParameter("file");
        File file = new File("/var/www/uploads/" + fileName);
        
        if (file.exists()) {
            FileInputStream fis = new FileInputStream(file);
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", 
                                                "attachment; filename=\"" + file.getName() + "\"");
            
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                response.getOutputStream().write(buffer, 0, bytesRead);
            }
            fis.close();
        } else {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
        }
    }
}

This servlet reads the file parameter from the request and creates a file object. Without proper validation, an attacker can exploit this to download arbitrary files from the server.

@WebServlet("/download")
public class FileDownloadServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
                                  throws ServletException, IOException {
        String fileName = sanitizeFileName(request.getParameter("file"));
        
        Path basePath = Paths.get("/var/www/uploads/");
        Path filePath = basePath.resolve(fileName).normalize();
        
        if (!filePath.startsWith(basePath)) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid file path");
            return;
        }
        
        File file = filePath.toFile();
        if (file.exists()) {
            FileInputStream fis = new FileInputStream(file);
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", 
                                                "attachment; filename=\"" + file.getName() + "\"");
            
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                response.getOutputStream().write(buffer, 0, bytesRead);
            }
            fis.close();
        } else {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
        }
    }

    private String sanitizeFileName(String fileName) {
        if (fileName == null 
                || fileName.contains("..") 
                || fileName.contains("/") 
                || fileName.contains("\\")) {
            throw new IllegalArgumentException("Invalid file name");
        }
        return fileName;
    }
}

In the secure version, the sanitizeFileName method ensures that the file name is free of traversal sequences, and the path is normalized and checked to prevent directory traversal.

CWE-22 (Path Traversal) is a critical vulnerability that can have serious consequences if not properly mitigated. In Java applications, it is important to use a combination of secure programming practices, input validation, canonicalization and access controls to protect against this threat. By understanding the nature of CWE-22 and implementing robust security measures, developers can significantly reduce the risk of unauthorized file access and protect their applications from potential exploitation.

Don't miss any news – follow us on Facebook, LinkedIn or Mastodon.

This article was originally published in German. It was translated with technical assistance and editorially reviewed before publication.