6

我试图在我的 Jetty 网络服务器盒子上积极主动地保护安全——特别是在存储 SSL 密钥信息方面,尽管我想要一个通用的解决方案。Apache 使用权限分离,因此它以 root 身份启动,因此它可以读取受保护的 SSL 密钥文件(和其他安全配置),然后切换到一些普通用户来实际服务器 HTTP 请求。但是 Java 没有这样做的机制。

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

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

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

  • 我在 Amazon EC2 下运行,因此我希望安全性尽可能自动化——即操作员不输入交互式密码。

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

感谢您提供任何信息。

编辑:

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

4

4 回答 4

3

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

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

于 2013-04-18T00:24:33.003 回答
1

一旦你将密码之类的东西放入源(存储在磁盘上),你就绕过了安全性。因此,将信息存储在 LDAP 中并没有帮助。

我也不相信 setuid 功能会有所帮助,因为它纯粹是为了访问网络代码中的端口,并且可能不会在正确的时间执行 setuid(在打开 SSL 文件之后)。当然,您可以通过以 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;
    }
}
于 2013-10-10T01:25:30.143 回答
1

我怀疑,由于 Java 的多平台特性,没有太多强调像 Apache Httpd 中存在的基于 Unix 的技术,因为这些技术不一定适用于 Java 可以运行的所有平台。

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

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

码头政策文件还指出:

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

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

一个明智的策略肯定会阻止 webapps 访问配置文件和密钥库。

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

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

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

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

于 2013-10-11T15:08:07.767 回答
0

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

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

  1. 启动并将一组机密信息从磁盘上的受根保护文件中读取到内存中。它应该立即加密内存中的秘密信息(见下文)。
  2. 从 root 切换到运行应用程序的非特权用户。
  3. 使用适当的应用程序参数分叉和执行的 JVM。
  4. 通过 STDIN 写入加密密钥和加密密码。
  5. 当 JVM 启动时,它会立即从 STDIN 中读取加密的秘密信息。
  6. 他们被读取的包装应用程序将终止。

作为扩展,小型 C 包装器可以通过使用基于 STDIN/STDOUT 的简单基于行的协议保持运行并提供对 JVM 对系统资源的访问。这将使 JVM 以受控方式访问系统上的其他受保护资源。

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

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

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

于 2013-10-11T13:10:41.803 回答