Placing Secure Data in a Java Web Application
The question is about security in tomcat, but first consider the following example:
Let's say you have an Apache web server. Then in the www folder create a folder called dist and under the dist folder create a folder called bdf23b1c-ddd3-4d5b-8fdf-948693674011
. In this folder, create several files with protected information:
www/dist/bdf23b1c-ddd3-4d5b-8fdf-948693674011/pic.png
This is obviously a bad idea, because anyone can go to yoursite.com/dist
and see everything in that folder.
On the other hand, suppose the same situation but with a dist folder created inside the tomcat web application. If you have the following in your web.xml:
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/dist/*</url-pattern>some java tool from outside
</servlet-mapping>
then you can safely download pic.png using your url. And if you go to yoursite.com/dist, tomcat will not show you the contents of this folder by default.
But my question is, how safe is it? Maybe you can just use some java tool from outside the server to connect to tomcat and figure out the full path of your valuable data somehow? I know about security in Java and this will be the final solution, but I don't want to use password based authentication.
UPD # 1: If any such hole (connection to some external Java tool or whatever) exists, post it here. Let's assume the default tomcat installation with ROOT app has been replaced with our app. Perhaps some settings for server.xml prohibit unwanted connections of "malicious java tools"?
By the way, I have already set up a firewall that rejects all traffic to the site except for http, https and ssh.
UPD # 2: As I mentioned in the comments, I want to create the cheapest alternative for Dropbox features. I don't want to use passwords because it might be inconvenient for our customers (we want to provide customers with a preview for our game)
source to share
Using a servlet doesn't make anything secure on its own. You don't need a Java tool to connect, you can even use Telnet, any scripting language, or create your own socket. Just use the download servlet somewhere and at least Basic authentication ("information hiding" is not a security aspect;).
"I want to create the cheapest alternative for Dropbox functionality ..."
Then use WebDav. Apache WebServer already has modules ready for sharing and data protection. There is no client like DropBox, which I assume, but you can connect your clients to a shared folder or whatever.
source to share
What you are doing is " security through obscurity ."
But it actually "works". Only someone who knows the URL will be able to download the file if you make sure the directory listing is denied at all times and that no link to the file exists somewhere where it could be found by a web browser like Google. And no one scans the media where you share the link with your gf.
This is definitely not completely safe, depending on the type of data you want to use, I would choose a different solution ...
source to share
First of all, you can configure Apache to not display directory contents (I'm surprised by the default (apparently)), so there is no need for Tomcat here.
As for connecting to Tomcat from outside, are you not going to do that? You will not leave your front door unlocked when you go to work.
Finally, the user will have to guess the path (and just care what is there), so it is unlikely to leak (except that you have to be careful with web browsers, as Marjay pointed out).
However, instead of relying on long critical paths, you can simply configure Basic Authentication in Apache. This way you don't need to worry about Tomcat or Java.
source to share
I liked Stefan Lindenberg's comment on using WebDav, I will definitely consider it. But I can't resist having to reinvent the wheel, so I was able to create this simple Java solution that will add even more obscurity to my "security" :) I would appreciate any comments and criticism for this little piece of code.
package com.test;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Anton P. Kolosov
*/
public class ObscureSecureServlet extends HttpServlet {
private static final Pattern UUID_PATTERN = Pattern.compile("^[A-F0-9]{8}(?:-[A-F0-9]{4}){3}-[A-F0-9]{12}$", Pattern.CASE_INSENSITIVE);
private String basePath;
/**
* Initialization routines
* @param config Servlet configuration
* @throws ServletException
*/
public void init(ServletConfig config) throws ServletException {
super.init(config);
basePath = config.getInitParameter("basePath");
}
/**
* Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
*
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String res = request.getParameter("res");
String name = request.getParameter("name");
Matcher matcher = UUID_PATTERN.matcher(res);
if (matcher.matches()) {
// Only UUIDs are allowed for res parameter
File file = new File(basePath + "/" + res, name);
if (file.exists()) {
sendFile(file, request, response);
return;
}
}
// Can redirect to jsp if you wish...
response.setContentType("text/html;charset=UTF-8");
try (PrintWriter out = response.getWriter()) {
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<title>File not found</title>");
out.println("</head>");
out.println("<body>");
out.println("File for res = " + res + " and name = " + name + " was not found!");
out.println("</body>");
out.println("</html>");
}
}
private void sendFile(File file, HttpServletRequest request, HttpServletResponse response) throws IOException {
long fileSize = file.length();
response.setHeader("Content-length", Long.toString(fileSize));
response.setContentType("application/octet-stream");
response.setHeader( "Content-Disposition", "filename=\"" + file.getName() + "\"" );
ServletOutputStream out = response.getOutputStream();
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(out);
write(file, bufferedOutputStream);
bufferedOutputStream.flush();
}
/**
* Writes a document to the passed stream.
* @param bufferedOutput The method writes name to this output stream
* @throws IOException IOException
*/
public void write(File file, BufferedOutputStream bufferedOutput) throws IOException {
byte buffer[] = new byte[1024 * 4];
BufferedInputStream bufferedInput = null;
try {
FileInputStream inputStream = new FileInputStream(file);
bufferedInput = new BufferedInputStream(inputStream);
int lengthRead = 0;
int offset = 0;
while (true) {
lengthRead = bufferedInput.read(buffer, 0, buffer.length);
if (lengthRead == -1) {
break;
}
bufferedOutput.write(buffer, 0, lengthRead);
offset += lengthRead;
}
} finally {
if (bufferedInput != null) {
bufferedInput.close();
}
}
}
// <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
/**
* Handles the HTTP <code>GET</code> method.
*
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Handles the HTTP <code>POST</code> method.
*
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Returns a short description of the servlet.
*
* @return a String containing servlet description
*/
@Override
public String getServletInfo() {
return "Servlet for 'obscure secure' file retrieving";
}// </editor-fold>
}
source to share
Security doesn't work thinking about what you can imagine, everything about what an attacker can imagine. Some ways to attack your circuit:
- Your computer or your girlfriend may be infected with a Trojan that shares every URL you visit with the help of an attacker.
- If you provide the URL to your friend by mail, the attack can intercept the mail and read the link if it is not encrypted. The NSA is doing this right now, by the way.
- If you are not using SSL (
https://
), all data will be sent over the Internet and any of the servers and browser can see what you are doing (and save a copy of the image). The NSA is doing this. - Your browser can share your URL with the browser provider as you type it. For example, Chrome does this if you don't configure your privacy settings correctly.
- If someone gains control of your server, they can see the file on your hard drive.
- If you are using the buggy version of SSH, an attacker could hack into your server and gain access to it like you.
These are just ideas from the top of my hat. In 2015, security, thinking about it on its own or "smart", no longer works. If you care about safety, you have to invest serious money and time for this to happen.
source to share