Secure file upload and validation
import java.io.File;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class UploadServlet extends HttpServlet {
private static final String UPLOAD_DIRECTORY = "upload";
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if(ServletFileUpload.isMultipartContent(request)){
try {
List multiparts = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
for(FileItem item : multiparts){
if(!item.isFormField()){
String name = new File(item.getName()).getName();
item.write( new File(UPLOAD_DIRECTORY + File.separator + name));
}
}
} catch (Exception ex) {
request.setAttribute("message", "File Upload Failed due to " + ex);
}
}else{
request.setAttribute("message", "Sorry this Servlet only handles file upload request");
}
request.getRequestDispatcher("/result.jsp").forward(request, response);
}
}
The above code is a simple servlet for handling file uploads in a Java web application using the Apache Commons FileUpload library.
The
doPost
method is where the vulnerability lies. It handles the file upload request without any checks for the file type or content.
The
if(ServletFileUpload.isMultipartContent(request))
statement checks if the incoming request is a multipart request, which is the type of HTTP request that is used for file uploads.
If it is a multipart request, the code proceeds to parse the request and extract the file items using
List multiparts = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
Then, for each file item, it checks if it is a form field or a file with
if(!item.isFormField())
. If it's a file, it gets the file name with
String name = new File(item.getName()).getName();
and writes the file to the upload directory with
item.write( new File(UPLOAD_DIRECTORY + File.separator + name));
The problem here is that there is no validation on the file type or content. This means that a user could upload a file with a double extension (e.g., .jpg.exe), or a file with a malicious extension (.html, .php, .exe), or even a file that contains malicious code. This could lead to various security issues, such as code execution, script injection, or even system takeover if the uploaded file is executed on the server.
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class UploadServlet extends HttpServlet {
private static final String UPLOAD_DIRECTORY = "secure_upload";
private static final List ALLOWED_EXTENSIONS = Arrays.asList("jpg", "png", "txt");
private static final long MAX_FILE_SIZE = 1024 * 1024 * 5; // 5MB
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if(ServletFileUpload.isMultipartContent(request)){
try {
List multiparts = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
for(FileItem item : multiparts){
if(!item.isFormField()){
String name = new File(item.getName()).getName();
String extension = name.substring(name.lastIndexOf(".") + 1);
// Check file extension
if (!ALLOWED_EXTENSIONS.contains(extension)) {
throw new Exception("File type not allowed");
}
// Check file size
if (item.getSize() > MAX_FILE_SIZE) {
throw new Exception("File size exceeds limit");
}
// Generate unique file name
String uniqueFileName = UUID.randomUUID().toString() + "." + extension;
// Write file to secure directory
File file = new File(UPLOAD_DIRECTORY + File.separator + uniqueFileName);
item.write(file);
// Set file permissions
Files.setPosixFilePermissions(Paths.get(file.getPath()), PosixFilePermissions.fromString("rwx------"));
}
}
} catch (Exception ex) {
request.setAttribute("message", "File Upload Failed due to " + ex);
}
}else{
request.setAttribute("message", "Sorry this Servlet only handles file upload request");
}
request.getRequestDispatcher("/result.jsp").forward(request, response);
}
}
The updated code includes several security measures to mitigate the risk of insecure file uploads:
1. File Type Validation: The code checks the file extension against a list of allowed extensions before uploading the file. Only files with "jpg", "png", and "txt" extensions are allowed.
2. File Size Restriction: The code checks the size of the file and throws an exception if the file size exceeds the maximum limit of 5MB.
3. Unique File Name: The code generates a unique file name for each uploaded file using a UUID. This prevents potential attacks that rely on predictable file names.
4. Secure File Storage: The uploaded files are stored in a secure directory outside of the web root directory. This prevents direct access to the files by users.
5. File Permissions: The code sets the file permissions to "rwx------", which means that only the owner of the file has read, write, and execute permissions.
Note: This code assumes a Unix-like operating system where you can set file permissions using the
Files.setPosixFilePermissions
method. If you're using a different operating system, you may need to use a different method to set file permissions.