【问题标题】:Placing secure data in Java web application在 Java Web 应用程序中放置安全数据
【发布时间】:2015-06-16 09:05:52
【问题描述】:

问题是关于tomcat中的安全性,但首先考虑下面的例子:

假设您有 apache 网络服务器。然后,在 www 文件夹下,创建名为 dist 的文件夹,并在文件夹 dist 下创建名为 bdf23b1c-ddd3-4d5b-8fdf-948693674011 的文件夹。在此文件夹下创建一些包含安全信息的文件:

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

这显然是个坏主意,因为任何用户都可以转到yoursite.com/dist 并查看此文件夹中包含的所有内容。

另一方面,假设相同的情况,但在 tomcat Web 应用程序中创建了文件夹 dist。如果您的 web.xml 中有以下内容:

<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/dist/*</url-pattern>some java tool from outside
</servlet-mapping>

然后您可以使用其 URL 安全地下载 pic.png。如果您访问 yoursite.com/dist,则默认情况下,tomcat 不会向您显示此文件夹的内容。

但我的问题是:它的安全性如何?也许您可以使用服务器外部的一些 java 工具连接到 tomcat 并以某种方式确定您宝贵数据的完整路径?我知道 java 的安全性,这将是最终的解决方案,但我不希望这里有任何基于密码的身份验证。

UPD #1:如果存在这样的漏洞(从外部连接一些 java 工具或其他),请在此处发布。假设使用我们的应用替换 ROOT 应用的默认 tomcat 安装。也许对 server.xml 进行一些调整以禁止“恶意 Java 工具”的不需要的连接?

顺便说一句,我已经设置了防火墙,它会拒绝除 http、https 和 ssh 之外的所有流量。

UPD #2:正如我在 cmets 中提到的,我想为 Dropbox 功能创建最便宜的替代方案。我不想使用密码,因为这会给我们的客户带来不便(我们希望为客户提供我们游戏的预发布访问权限)

【问题讨论】:

    标签: java security tomcat


    【解决方案1】:

    你正在做的是“security through obscurity”。

    但实际上它会“工作”。只有知道 URL 的人才能下载该文件,只要您确保目录列表始终被拒绝,并且该文件的链接不存在于某个地方,可以被 Google 之类的网络爬虫获取。并且没有人会扫描您与 gf 共享链接的媒体。

    这绝对不是完全安全的,取决于您要共享的数据类型,我会选择不同的解决方案...

    【讨论】:

    • 感谢您的回答。是的,它以最纯粹的形式通过默默无闻来实现安全 :) 我想要实现的有点像 dropbox 的功能,但实现起来很简单。也许我应该创建一些 servlet,使用 guid 作为数据键从配置的文件夹下载数据......我只是想在这里使用最便宜的解决方案。至于数据,这将是我们创建的分发包,我们希望通过在电子邮件中向他们提供这个“安全”链接来与我们的一些客户共享。
    【解决方案2】:

    首先,您可以将 Apache 配置为不显示目录内容(我很惊讶它默认是打开的(显然)),所以这里不需要 Tomcat。

    至于从外部连接到 Tomcat,你不会允许的吧?你上班时也不会把前门锁着。

    最后,用户必须猜测路径(并且足够关心那里有什么),所以它不太可能泄露出去(除非你必须小心网络爬虫,正如 Marged 指出的那样)。

    不过,您可以只在 Apache 中配置基本身份验证,而不是依赖冗长的神秘路径。这样您就不必担心 Tomcat 或 Java。

    【讨论】:

    • 谢谢。我使用 tomcat 不是因为它不显示目录内容,而是因为我们的网站是用 Java 实现的 :) 正如我所提到的,我想为类似 Dropbox 的功能实现最简单的解决方案。我们的客户可能不方便输入一些用户名和密码。当然我不想让外部访问 tomcat,但我只是担心在这里修复所有可能的安全漏洞。
    【解决方案3】:

    使用 servlet 本身不会产生任何东西 secure。您不需要 Java 工具来连接,您甚至可以使用 Telnet、任何脚本语言或创建自己的套接字。只需从某个地方使用下载 servlet,至少 Basic authentication (“信息隐藏”不是安全方面;)。

    “我想为 Dropbox 功能创建最便宜的替代方案……”

    然后使用 WebDav。 Apache WebServer 已经有准备好共享和保护数据的模块。我猜没有像 DropBox 这样的客户端,但您可以让您的客户端连接到共享文件夹或其他东西。

    【讨论】:

      【解决方案4】:

      我喜欢 Stefan Lindenberg 关于 WebDav 用法的评论,我一定会调查它。但我无法抗拒重新发明轮子,所以我设法创建了这个简单的 Java 解决方案,它为我的“安全性”增加了更多的模糊性:) 我会感谢任何 cmets 和对这段代码的批评。

      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>
      
      }
      

      【讨论】:

      • 您可以将其复制到 codereview 以获得更多反馈
      • 感谢您的建议!也许你可以提供链接,我是这里的新人:)
      • 再次感谢!我试试看。
      【解决方案5】:

      安全并不能通过思考你能想象到的东西来发挥作用,它完全取决于攻击者能想象到的东西。攻击您的计划的一些方法:

      • 您或您女朋友的计算机可能感染了木马,该木马与攻击者共享您访问的每个 URL
      • 如果您通过邮件将 URL 提供给您的女朋友,攻击可以拦截邮件并读取链接,除非它是加密的。顺便说一句,美国国家安全局现在就是这样做的。
      • 如果您不使用 SSL (https://),所有数据都将通过 Internet 发送,服务器和浏览器之间的任何人都可以看到您在做什么(并保存图像的副本)。 NSA 就是这样做的。
      • 您的浏览器可能会在您键入 URL 时与浏览器供应商共享您的 URL。例如,除非您正确配置隐私选项,否则 Chrome 会这样做。
      • 如果有人控制了您的服务器,他们可以看到硬盘上的文件。
      • 如果您使用有缺陷的 SSH 版本,攻击者可以破解您的服务器并像您一样访问它。

      这些只是我的想法。在 2015 年,通过自己思考或“聪明”来确保安全不再起作用。如果您关心安全性,就必须投入大量时间和金钱来实现它。

      【讨论】:

      • 哦……谢谢,亚伦。你所有的观点当然是正确的。只是好奇,保管箱如何设法使他们的“共享文件”按钮工作。你知道,点击这个按钮,它会将一些模糊的 URL 复制到你的剪贴板,你可以通过电子邮件发送......当然,如果你的数据真的很重要,你不应该这样做。
      猜你喜欢
      • 1970-01-01
      • 2014-09-19
      • 2015-10-26
      • 2011-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多