如何用Java Web服务器实现“权限分离”的一些措施?

时间:2013-04-17 22:48:58

标签: java ssl jetty privileges

我正在尝试主动关注我的Jetty网络服务器盒的安全性 - 特别是关于存储SSL密钥信息,尽管我想要一个通用的解决方案。 Apache使用privilege separation,以便它以root身份启动,以便它可以读取受保护的SSL密钥文件(和其他安全配置),然后切换到一些普通用户以实际服务HTTP请求。但是Java没有这样做的机制。

有关如何在Java Web应用程序中实现相同级别的安全性的任何建议?我的要求包括:

  • 秘密信息只能由root读取。

  • 不应将任何解锁密钥等的密码配置到代码中,以便与服务器具有相同用户级权限的人无法轻松获取密码。

  • 我在Amazon EC2下运行,所以我希望安全性尽可能自动化 - 即运营商不输入交互式密码。

一种可能性是使用~LDAP将秘密信息与应用程序分开,并仅将LDAP访问用户名/密码烘焙到应用程序中。但我正在寻找更好的解决方案。

感谢您提供任何信息。

修改

我希望解决方案涵盖SSL,但考虑到我想限制访问的其他秘密。我在最初的帖子中没有说清楚。

4 个答案:

答案 0 :(得分:3)

您描述的apache技术由可选的 jetty-setuid 功能提供。

请参阅http://www.eclipse.org/jetty/documentation/current/setuid.html

答案 1 :(得分:1)

只要将密码(如存储在磁盘上)中的密码烘焙,就可以规避安全性。因此,将信息存储在LDAP中无济于事。

我不相信setuid功能也会有所帮助,因为它纯粹用于访问网络代码中的端口,并且可能无法在正确的时间(打开SSL文件之后)执行setuid。当然,您可以通过以root身份保护文件来测试它,看看它是否可以打开它们......如果是这样的话,那你就是金色的,而Joakim的答案是最好的选择。

我们所做的是设置一个简单的apache或nginx服务器,通过代理在JVM前面,然后在它自己的UID下运行jetty。然后,您可以利用已经在这两个服务器中经过充分测试的setuid SSL安全性。我们还有一些其他要求,这也有助于解决,但我可能会选择这样做,即使我们没有。

nginx配置也非常简单:

server {
    listen       192.168.1.1:443;
    server_name  www.mydomain.com;
    index  index.html index.htm;
    root   /usr/share/nginx/html;

    ssl                  on;
    ssl_certificate      /etc/nginx/conf.d/ssl/server.crt;
    ssl_certificate_key  /etc/nginx/conf.d/ssl/server.key;
    access_log  /var/log/nginx/ssl.access.log  main;

    ssl_session_timeout  5m;

    ssl_protocols  SSLv2 SSLv3 TLSv1;
    ssl_ciphers  HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers   on;

    location /AppPath {
       proxy_pass http://jettyhost:8080/AppPath;
    }
}

答案 2 :(得分:1)

我怀疑,由于Java的多平台特性,人们并没有太多关注基于Unix的技术,比如Apache Httpd中存在的技术,因为这些技术可能不一定适用于所有Java平台。可以跑。

在Java中,权限分离是通过安全管理器内置的。这是否与root / non-root分离一样好,我不确定(总会有bug)。但是,它的策略原则上能够表达更细微的访问规则,而不仅仅是根用户和非root用户之间的区别。

Jetty有这个,但这显然是dropped in Jetty 9(您仍然可以使用安全管理器,但您可能必须编写自己的策略并执行更多工作以在容器中实现它们)。

Jetty Policy document还声明:

  

通常,用户信任他们正在开发的应用程序或信任足以将webapp部署到jetty实例中。如果您不知道需要使用安全管理器设置,则可能不需要。

我不确定我是否同意这一点。事实上,如果有一个webapp我怀疑是恶意的,我无论如何都不会运行它,但使用安全管理器也是为了遏制潜在的安全漏洞。任何人都可以写错误,甚至是优秀的程序员。拥有一个限制webapps可能的操作的机制当然是一件好事。

合理的策略肯定会阻止webapps访问配置文件和密钥库。

我还认为这个webapp分离也是“容器”概念的核心(虽然安全性只是这种分离的目的之一,但它似乎已经丢失了。)

公平地说,它不像Apache Httpd中root / forking机制提供的分离那么简单。 Java安全策略的强大功能也带来了复杂性。我想这些功能一般都不太清楚,因此很少使用。使用Apache Httpd(或Nginx或其他)作为反向代理往往是一种更简单的解决方案,用于保护私钥。

您可以考虑的另一种方法是使用PKCS#11密钥库。这是由JRE支持的。硬件安全模块有助于防止复制私钥。 (从Java的角度来看,您将获得一个PrivateKey实例,该实例将加密操作委派给PKCS#11库,但您根本无法提取私有数据。)

当然,当您无法访问硬件时,这是一个问题,但有软件实现(查找“软件HSM”可能看起来很奇怪,但它会带来一些结果)。并非所有这些都会让你拥有你所拥有的分离,但有些应该(有效地,通过与另一个持有私钥的实体进行通信,你可以作为另一个用户运行)。我没有尝试过,但this one可能会引起人们的兴趣。

答案 3 :(得分:0)

虽然我很欣赏@Joakim和@Tony的答案,但我希望有一个更通用的解决方案,它涵盖了没有JNI / Jetty特定功能的通用密码保护,而且不仅仅是SSL密钥保护。

我能想到的最好的解决方案是一个小的C包装程序,它是setuid root。它会:

  1. 启动并从磁盘上的根保护文件中读取一组秘密信息到内存中。它应该立即加密内存中的秘密信息(见下文)。
  2. 从root用户切换到运行应用程序的非特权用户。
  3. Forks和exec的JVM以及相应的应用程序参数。
  4. 将加密密钥和加密密码写入via STDIN。
  5. 当JVM启动时,它会立即从STDIN读取加密的机密信息。
  6. 他们被阅读,包装应用程序将终止。
  7. 作为扩展,通过在STDIN / STDOUT上使用简单的基于行的协议,小型C包装器可以保持运行并提供对JVM到系统资源的访问。这将使JVM以受控方式访问系统上的其他受保护资源。

    以下是关于可能解决方案的其他一些想法。

    • 服务在启动时由init.d脚本启动,该脚本以root身份运行,并通过某些Web服务或某些FIFO文件或其他内容提供给JVM的一组密钥。在第一次请求之后,它将关闭或在启动后的几秒钟后。
    • LDAP肯定比在盒子本身上拥有秘密foo更好 - 应用程序用户可以读取。作为上述解决方案的改动,setuid程序可以将LDAP密码注入应用程序,因此它不会存在于用户可读空间中。

    与往常一样,两个应用程序都需要保护内存中的密码。将它们存储在系统套接字中或将它们分成不连续的内存块总是一个好主意。您也可以生成一个密钥并在内存中加密它们。