Anton P. Kolosov
Anton P. Kolosov

Reputation: 43

Placing secure data in Java web application

The question is about security in tomcat, but first consider the following example:

Suppose you have apache web server. Then, under www folder, create folder named dist, and under folder dist create folder named bdf23b1c-ddd3-4d5b-8fdf-948693674011. Under this folder create some files with secure information:

www/dist/bdf23b1c-ddd3-4d5b-8fdf-948693674011/pic.png

This is obviously bad idea, because any user can go to the yoursite.com/dist and see everything contained in this folder.

On the other hand, suppose the same situation, but with folder dist created inside 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 its URL. And if you go to yoursite.com/dist, then by default tomcat don't show you contents of this folder.

But my question is: how it is secure? Maybe you can just use some java tool from outside of the server to connect to tomcat and somehow determine full path of your precious data? I know about security in java and it will be the ultimate solution, but I don't want any password-based authentication here.

UPD #1: If some such hole (connecting with some java tool from outside or whatever) exist, please post it here. Suppose default tomcat installation with ROOT app replaced with our app. Maybe some tweaks to server.xml to forbid the unwanted connections of "malicious java tools"?

BTW, I already set up the 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 possible alternative for dropbox functionality. I don't want to use passwords, because it can be inconvenient for our clients (we want to give the clients the pre-release access for our game)

Upvotes: 3

Views: 198

Answers (5)

Aaron Digulla
Aaron Digulla

Reputation: 328536

Security doesn't work by thinking about what you can imagine, it's all about what an attacker could imagine. Some ways to attack your scheme:

  • Your computer or that of your girlfriend could be infected with a trojan which shares every URL which you visit with the attacker
  • If you give the URL to your girlfriend by mail, an attack can intercept the mail and read the link unless it's encrypted. The NSA does that right now, btw.
  • If you're not using SSL (https://), all the data will be sent over the Internet and anyone between the server and the browser can see what you're doing (and save a copy of the image). NSA does that.
  • Your browser might share your URL with the browser vendor as you type it. Chrome does that, for example, unless you configure the privacy options right.
  • If someone got control over your server, they can see the file on the hard disk.
  • If you use a buggy version of SSH, an attacker can crack your server and get access to it as you.

These are just ideas from the top of my hat. In 2015, security by thinking about it yourself or "being clever" doesn't work anymore. If you care about security, you have to invest serious time and money to make it happen.

Upvotes: 0

Anton P. Kolosov
Anton P. Kolosov

Reputation: 43

I liked Stefan Lindenberg comment about the WebDav usage, I will definitely look into it. But I cannot resist reinventing the wheel, so I managed to create this simple Java solution, which adds more obscurity to my "security" :) I would be grateful for any comments and criticism for this small 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>

}

Upvotes: 1

Stefan
Stefan

Reputation: 12453

Using a servlet does not make anything secure by itself. You dont need a Java tool to connect, you can even use Telnet, any scripting language or create your own socket. Just use a download servlet from somewhere and at least Basic authentication ("information hiding" is no security aspect ;).

"I want to create the cheapest possible alternative for dropbox functionality ..."

Use WebDav then. Apache WebServer already has modules ready to share and secure data. There is no client like the DropBox one I guess, but you can make your clients connect to a shared folder or something.

Upvotes: 2

Kayaman
Kayaman

Reputation: 73528

First of all, you can configure Apache not to show the directory contents (I'm surprised it's on by default (apparently)), so there's no need for Tomcat here.

As for connecting to Tomcat from the outside, you're not going to allow that are you? You wouldn't leave your front door unlocked when you go to work either.

Lastly, the user would have to guess the path (and care enough about what's in there), so it's not very likely to leak out (except you've got to be careful about the webcrawlers as Marged pointed out).

Still, instead of relying on long cryptic paths, you could just configure Basic Authentication in Apache. That way you don't need to worry about Tomcat or Java.

Upvotes: 3

Marged
Marged

Reputation: 10953

What you are doing is "security through obscurity".

But in fact it will "work". Only the person knowing the URL will be able to download the file, as long as you make sure that directory listing is denied all time and that no link to the file exists somewhere where it could be picked up by a webcrawler like Google. And that nobody does scan the media where you share the link with your gf.

It is definitely not totally safe, depending on the type of data you want to share I would go for a different solution ...

Upvotes: 3

Related Questions