博客 | NGINX

使用 NGINX 安全分发 SSL 私钥

NGINX-F5-horiz-black-type-RGB 的一部分
欧文·加勒特缩略图
欧文·加勒特
2019 年 4 月 2 日发布

这篇博文介绍了 NGINX 在托管 SSL 加密网站时使用的安全分发 SSL 私钥的几种方法。 它解释道:

对于许多部署来说,标准方法就足够了。 本文讨论的两种更复杂的方法阻止了攻击者获取 SSL 私钥的其他方式。 我们还将在后续文章中介绍更多技术:

  • 使用第三方秘密存储库(如 HashiCorp Vault)安全地分发密码
  • 自动将证书从 Vault 配置到 NGINX Plus 的键值存储,这样私钥材料就不会存储在磁盘上

本文介绍的方法适用于需要管理自己的密钥并创建自己的安全密钥分发策略的用户。 对于在已经与秘密存储集成的环境中运行 NGINX 的用户(例如Kubernetes )来说,它们不是必需的。

这篇文章适用于 NGINX Open Source 和 NGINX Plus。 为了方便阅读,我们将始终引用NGINX

编辑者——这篇文章是关于在 NGINX 中保护 SSL 私钥的系列文章中的第一篇。另请参阅本系列的其他文章:

 

为什么要保护 SSL 私钥?

SSL/TLS 用于验证、加密和验证网络交易的完整性。 网站使用由证书颁发机构 (CA) 签名的公共证书进行身份验证,并通过使用相应的私钥(必须保密)执行计算来证明其拥有该证书。

如果私钥被泄露(泄露给另一个实体),则存在两个主要风险。

  • 风险1: 冒充。 拥有私钥的攻击者可以拦截网络流量,然后发起中间人(MITM) 攻击。 这次攻击会捕获并解密所有流量,甚至可能对其进行修改,而客户端或网站却毫不知情。
  • 风险2: 解密。 拥有私钥并记录了网络流量的攻击者就可以离线解密网络流量。 请注意,此攻击不能用于使用完美前向保密(PFS) 密码的连接。

如果私钥被泄露,您唯一的办法就是联系 CA 并请求撤销您的证书;然后您必须依靠客户端来检查并遵守撤销状态。

此外,使用有效期较短的证书是一种很好的做法(例如, Let's Encrypt证书在 90 天后过期)。 在证书即将过期之前,您需要生成新的私钥并从 CA 获取新的证书。 一旦私钥被泄露,这将减少您的风险。

NGINX 安全边界

哪些人员和流程可以访问 NGINX 中的 SSL 私钥?

首先,任何获得运行 NGINX 的服务器root访问权限的用户都能够读取和使用 NGINX 本身使用的所有资源。 例如,有已知方法可以从正在运行的进程的内存中提取 SSL 私钥。

因此,无论私钥如何存储和分发,都无法保护私钥免受主机服务器上具有root权限的攻击者的攻击。

接下来,任何可以修改和提交 NGINX 配置的用户都可以以多种方式使用该权限——打开对内部服务的代理访问、绕过身份验证措施等。 他或她可以修改 NGINX 配置以获取服务器的root访问权限(或同等权限),尽管SELinuxAppArmor等工具有助于减轻这种可能性。

因此,通常无法保护私钥免受能够修改和提交 NGINX 配置的攻击者的攻击。

幸运的是,任何有能力的组织都有完善的安全流程,使攻击者难以获得root权限或修改 NGINX 配置。

但是,权限较低的攻击者还可以通过另外两种方式获取私钥访问权限:

  • 用户可能有正当理由需要查看 NGINX 配置,或者可能获取配置数据库或备份的访问权限。 NGINX 私钥通常存储在配置中。
  • 用户可能通过虚拟机管理程序或系统备份获得对 NGINX 服务器文件系统的访问权限。 文件系统中存储的任何数据(包括私钥材料)都有可能被访问。

本文档中描述的过程封堵了这两种攻击方法。

标准 NGINX 配置

我们首先回顾一下带有 SSL/TLS 的典型 NGINX 配置:

服务器 { 监听 443 ssl;服务器名称 a.dev0; ssl_certificate ssl/a.dev0.crt; ssl_certificate_key ssl/a.dev0.key;位置 / { 返回 200 "来自服务 A\n 的 Hello";}}

SSL 公共证书( a.dev0.crt )和私钥( a.dev0.key )存储在文件系统的/etc/nginx/ssl/中。 私钥仅由 NGINX 主进程读取,该进程通常以root身份运行,因此您可以对其设置最严格的访问权限:

root@web1:/etc/nginx/ssl# ls -l a.dev0.key -r-------- 1 root root 1766 8月 15 16:32 a.dev0.key

私钥必须始终可用;每当 NGINX 软件启动、重新加载配置或执行语法检查( nginx -t )时,NGINX 主进程都会读取它。

有关配置 SSL/TLS 的更多信息,请参阅NGINX Plus 管理指南

标准配置的安全隐患

如上所述,攻击者可以获得对运行 NGINX 软件的容器、虚拟机或服务器的root访问权限,从而读取 SSL 私钥。

加密 SSL 私钥

NGINX 支持加密私钥,使用 AES256 等安全算法:

root@web1:/etc/nginx/ssl# mv a.dev0.key a.dev0.key.plain root@web1:/etc/nginx/ssl# openssl rsa -aes256 -in a.dev0.key.plain -out a.dev0.key写入 RSA 密钥 输入 PEM 密码:安全密码验证 -再次输入 PEM 密码:安全密码

当你启动 NGINX,或者重新加载或测试 NGINX 配置时,NGINX 将以交互方式请求解密密码:

root@web1:/etc/nginx# nginx -t Enter PEM pass phrase:安全密码nginx:配置文件/etc/nginx/nginx.conf语法正确 nginx:配置文件/etc/nginx/nginx.conf测试成功

使用 SSL 密码文件

以交互方式输入密码不方便且难以实现自动化,但您可以配置 NGINX 以使用存储在由ssl_password_file指令命名的单独文件中的密码列表。 当 NGINX 需要读取私钥时,它会尝试依次使用文件中的每个密码来解密密钥。 如果所有密码均无效,NGINX 将拒绝启动。

ssl_password_文件 /var/lib/nginx/ssl_passwords.txt;

ssl_password_file必须与配置分开分发,并且只有root用户可读。 您可以将其视为放置在受信任服务器上的授权令牌。 NGINX 仅当在具有授权令牌的服务器上运行时才能解密私钥。

单独文件中加密密钥的安全隐患

这种方法使得 NGINX 配置本身对于攻击者来说毫无用处,从而减少了攻击面。 攻击者还必须获取ssl_password_file的内容。

如果攻击者确实获得了存储ssl_password_file的文件系统的访问权限(例如,从备份或通过主机系统),他或她可以读取该文件并使用密码解密 SSL 私钥。

您可以通过将ssl_password_file存储在 RAM 磁盘或tmpfs上来降低这种风险。 这种存储通常不太容易被外部攻击者访问(例如,在服务器重启时它会被清除)并且可以从系统备份中排除。 您需要确保密码文件在系统启动时初始化。

更安全地分发 SSL 密码列表

以下过程描述了一种从中央分发点分发 SSL 密码列表的更安全的方法。

每当 NGINX 需要解密 SSL 密钥时,它都会查询中央分发点并使用密码,而不会将其存储在本地磁盘上。 为了向中央密码服务器进行身份验证,NGINX 实例使用一个令牌,您可以随时撤销该令牌以切断对密码的访问。

创建中央密码分发点

首先创建密码分发点 (PDP)。 对于这个简单的实现,我们使用 HTTPS 服务来传递密码列表,通过用户名和密码进行验证:

$ curl -u dev0:mypassword https://pdpserver.local/ssl_passwords.txt密码1 密码2...

然后,您可以根据需要在 PDP 上添加或删除身份验证令牌来启用或撤销访问权限。 您可以使用 NGINX 等 Web 服务器实现密码分发服务器,并使用适当的任何类型的身份验证令牌。

接下来,我们需要设置 NGINX 来从 PDP 检索密码。 我们首先创建一个名为connector.sh的shell脚本,其内容如下:

#!/bin/sh
# 用法:connector.sh

CONNECTOR=$1
CREDS=$2
PDP_URL=$3

[ -e $CONNECTOR ] && /bin/rm -f $CONNECTOR

mkfifo $CONNECTOR; chmod 600 $CONNECTOR

while true; do
curl -s -u $CREDS -k $PDP_URL -o $CONNECTOR
done

该脚本需要作为后台进程运行,调用方式如下:

root@web1:~# ./connector.sh /var/run/nginx/ssl_passwords \dev0:mypassword https://pdpserver.local/ssl_passwords.txt &

连接器连接到指定的本地路径( /var/run/nginx/ssl_passwords ),然后使用ssl_password_file指令配置 NGINX 以访问该路径:

ssl_password_文件/var/run/nginx/ssl_passwords;

通过读取连接器路径来测试连接器:

root@web1:~# cat /var/run/nginx/ssl_passwords密码1 密码2 ...

验证 NGINX 是否可以读取密码并解密 SSL 密钥:

root@web1:~# nginx -t nginx: 配置文件 /etc/nginx/nginx.conf 语法正确 nginx: 配置文件 /etc/nginx/nginx.conf 测试成功

您可以使用中央 PDP 方法来安全地分发 NGINX 通常从磁盘读取的任何资源,例如个人私钥或其他敏感数据。

PDP 的安全隐患

与将 SSL 密码存储在磁盘上相比,此解决方案具有多项优势:

  • SSL 密码从未存储在服务器的文件系统中,因此有权访问文件系统的攻击者无法直接访问它们。
  • 密码从中央接入点分发,使得监控和审计更容易执行。
  • 可以集中控制各个服务器的访问。 例如,一旦服务器退役,您就可以撤销其访问令牌。

请注意,有权访问文件系统的用户可能会提取用于访问 PDP 的凭据。 当不再需要这些凭证时,撤销它们非常重要。

概括

保护 SSL 私钥不被泄露的方法有很多,而且安全性和复杂程度也不断提高:

  • 对于大多数组织来说,限制对运行 NGINX 的环境的访问就足够了,这样未经授权的用户就无法获得root访问权限并且无法查看 NGINX 配置。
  • 对于某些环境,可能无法完全限制对 NGINX 配置的访问,因此可以使用 SSL 密码文件。
  • 在有限的情况下,组织可能希望确保密钥和密码永远不会存储在磁盘上。 密码分发点流程说明了此解决方案的概念证明。

本系列的其他文章介绍了保护 SSL 密钥的其他步骤:

亲自尝试 NGINX Plus – 立即开始30 天免费试用联系我们讨论您的用例


“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”