这篇博文介绍了 NGINX 在托管 SSL 加密网站时使用的安全分发 SSL 私钥的几种方法。 它解释道:
对于许多部署来说,标准方法就足够了。 本文讨论的两种更复杂的方法阻止了攻击者获取 SSL 私钥的其他方式。 我们还将在后续文章中介绍更多技术:
本文介绍的方法适用于需要管理自己的密钥并创建自己的安全密钥分发策略的用户。 对于在已经与秘密存储集成的环境中运行 NGINX 的用户(例如Kubernetes )来说,它们不是必需的。
这篇文章适用于 NGINX Open Source 和 NGINX Plus。 为了方便阅读,我们将始终引用NGINX 。
编辑者——这篇文章是关于在 NGINX 中保护 SSL 私钥的系列文章中的第一篇。另请参阅本系列的其他文章:
SSL/TLS 用于验证、加密和验证网络交易的完整性。 网站使用由证书颁发机构 (CA) 签名的公共证书进行身份验证,并通过使用相应的私钥(必须保密)执行计算来证明其拥有该证书。
如果私钥被泄露(泄露给另一个实体),则存在两个主要风险。
如果私钥被泄露,您唯一的办法就是联系 CA 并请求撤销您的证书;然后您必须依靠客户端来检查并遵守撤销状态。
此外,使用有效期较短的证书是一种很好的做法(例如, Let's Encrypt证书在 90 天后过期)。 在证书即将过期之前,您需要生成新的私钥并从 CA 获取新的证书。 一旦私钥被泄露,这将减少您的风险。
哪些人员和流程可以访问 NGINX 中的 SSL 私钥?
首先,任何获得运行 NGINX 的服务器root
访问权限的用户都能够读取和使用 NGINX 本身使用的所有资源。 例如,有已知方法可以从正在运行的进程的内存中提取 SSL 私钥。
因此,无论私钥如何存储和分发,都无法保护私钥免受主机服务器上具有root
权限的攻击者的攻击。
接下来,任何可以修改和提交 NGINX 配置的用户都可以以多种方式使用该权限——打开对内部服务的代理访问、绕过身份验证措施等。 他或她可以修改 NGINX 配置以获取服务器的root
访问权限(或同等权限),尽管SELinux和AppArmor等工具有助于减轻这种可能性。
因此,通常无法保护私钥免受能够修改和提交 NGINX 配置的攻击者的攻击。
幸运的是,任何有能力的组织都有完善的安全流程,使攻击者难以获得root
权限或修改 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 私钥。
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测试成功
以交互方式输入密码不方便且难以实现自动化,但您可以配置 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 密码列表的更安全的方法。
每当 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 通常从磁盘读取的任何资源,例如个人私钥或其他敏感数据。
与将 SSL 密码存储在磁盘上相比,此解决方案具有多项优势:
请注意,有权访问文件系统的用户可能会提取用于访问 PDP 的凭据。 当不再需要这些凭证时,撤销它们非常重要。
保护 SSL 私钥不被泄露的方法有很多,而且安全性和复杂程度也不断提高:
root
访问权限并且无法查看 NGINX 配置。本系列的其他文章介绍了保护 SSL 密钥的其他步骤:
亲自尝试 NGINX Plus – 立即开始30 天免费试用或联系我们讨论您的用例。
“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”