Local file inclusion - Java

Local file inclusion - Java

Need

Prevention of unauthorized access to sensitive files on the server

Context

  • Usage of Java 8 for developing applications with enhanced features and performance
  • Usage of javax.servlet-api for Java Servlet development

Description

Non compliant code

        import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class VulnerableServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String fileName = request.getParameter("file");

        ServletContext context = getServletContext();
        InputStream is = context.getResourceAsStream("/" + fileName);

        if (is != null) {
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader reader = new BufferedReader(isr);
            PrintWriter writer = response.getWriter();
            String text = "";

            while ((text = reader.readLine()) != null) {
                writer.println(text);
            }
        }
    }
}
        
        

In the code above, the doGet method of the servlet takes a request parameter named file and uses it to read a file from the application's context. The fileName is directly concatenated to the base path "/" without any validation or sanitization. This means an attacker can manipulate the file parameter to read any file that the application has access to, such as configuration files, source code, or any other sensitive information stored on the server.

For instance, an attacker can send a request like http://example.com/VulnerableServlet?file=../../etc/passwd to read the /etc/passwd file which contains user account information on Unix-like systems.

This vulnerability is known as Local File Inclusion (LFI) and can lead to information disclosure, denial of service, or even remote code execution if the attacker can include a file that contains malicious code.

Steps

  • Validate user input: Implement input validation to ensure that the 'file' parameter contains only allowed characters and does not allow any path traversal sequences.
  • Whitelist allowed files: Maintain a whitelist of allowed files that can be accessed by the application. Check if the requested file is present in the whitelist before accessing it.
  • Use absolute paths: Instead of using relative paths, use absolute paths to access files on the server. This ensures that the application can only access files within the intended directory.
  • Implement access controls: Restrict access to sensitive files by implementing proper access controls. Only allow access to files that are necessary for the application's functionality.
  • Sanitize user input: If it is necessary to allow user input for file names, sanitize the input to remove any potentially harmful characters or sequences.
  • Implement file permissions: Set appropriate file permissions on the server to restrict access to sensitive files from the application.
  • Regularly update and patch dependencies: Keep all libraries and frameworks used by the application up to date to ensure that any known vulnerabilities are patched.
  • Perform security testing: Regularly conduct security testing, including penetration testing and code reviews, to identify and fix any vulnerabilities in the application.

Compliant code

        import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.Arrays;
import java.util.List;

public class SecureServlet extends HttpServlet {

    private static final List ALLOWED_FILES = Arrays.asList("file1.txt", "file2.txt", "file3.txt");

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String fileName = request.getParameter("file");

        // Validate user input
        if (fileName == null || fileName.contains("..") || !ALLOWED_FILES.contains(fileName)) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }

        ServletContext context = getServletContext();
        InputStream is = context.getResourceAsStream("/" + fileName);

        if (is != null) {
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader reader = new BufferedReader(isr);
            PrintWriter writer = response.getWriter();
            String text = "";

            while ((text = reader.readLine()) != null) {
                writer.println(text);
            }
        }
    }
}
        
        

The above code fixes the Local File Inclusion vulnerability in the following ways:

1. Validate user input: The code checks if the 'file' parameter is null, contains path traversal sequences (..), or is not present in the whitelist of allowed files. If any of these conditions are met, it sends a bad request error (400) and returns.

2. Whitelist allowed files: A list of allowed files is maintained. The requested file is checked against this list before it is accessed.

3. Use absolute paths: Although not explicitly shown in the code, it is recommended to use absolute paths when accessing files on the server. This can be done by configuring the server or application settings.

4. Implement access controls: Access to sensitive files should be restricted by implementing proper access controls. This can be done at the server or operating system level.

5. Sanitize user input: The code checks for path traversal sequences and removes them if found. This is a form of input sanitization.

6. Implement file permissions: File permissions should be set on the server to restrict access to sensitive files. This can be done at the server or operating system level.

7. Regularly update and patch dependencies: Although not shown in the code, it is important to keep all libraries and frameworks used by the application up to date.

8. Perform security testing: Regular security testing should be conducted to identify and fix any vulnerabilities. This includes penetration testing and code reviews.

References