我有一个客户端证书,其主题中包含非英文字符,当我使用 ngx_http_ssl_module 的属性 $ssl_client_s_dn 时,该值被转义两次。
实际价值 = “L=D'UNCONGÉ”
\xC3 再次编码得到 \x5CC3 ,我需要避免这种情况
实际值 = “L=D'UNCONG\x5CC3\x5C89”
预期值 = “L=D'UNCONG\xC3\x89”
有人可以告诉我我做错了什么吗?我该如何避免这种情况。
我有一个客户端证书,其主题中包含非英文字符,当我使用 ngx_http_ssl_module 的属性 $ssl_client_s_dn 时,该值被转义两次。
实际价值 = “L=D'UNCONGÉ”
\xC3 再次编码得到 \x5CC3 ,我需要避免这种情况
实际值 = “L=D'UNCONG\x5CC3\x5C89”
预期值 = “L=D'UNCONG\xC3\x89”
有人可以告诉我我做错了什么吗?我该如何避免这种情况。
这个谜题有很多部分。
首先,我要说的是,您提到的“预期值”包括十六进制数字常量(格式:\xHH)。只有在这是默认编码的情况下,这才是“预期”的。
Nginx 本身使用大多数字符串文字和数字常量(如十六进制、八进制或 Unicode 表示),在配置中没有任何问题;读取标头时也是如此 - 所以我最初对这种“双重转义”的不寻常格式感到困惑。
我查看了有关证书可分辨名称 (DN) 编码的 RFC。证书字符串的格式与双重编码的 URL 字符串等相比有细微的差别,但这种差异很重要。
请注意 RFC,特别是第 5 节末尾的示例表,并查看“引用”值与 UTF-8: https: //datatracker.ietf.org/doc/html/rfc2253#section-5
RFC 指出,证书 UTF-8“É”(0xC389)在引用时为 \C3\89。根据您报告的实际值,这确认 => Nginx 正在从证书本身读取引用值。
然后 Nginx 对 \C3\89 中的“\”进行转义,这就解释了为什么它会变成“实际值”中的 \x5CC3\x5C89。
然而,这种不寻常的表示(可能)仅限于 Nginx 本身的日志 - 取决于您的配置,它可能不是 $ssl_client_s_dn 的实际值。
默认情况下,Nginx 会转义日志中的字符串,但不会转义变量中的值。关于日志格式,您可以通过向 log_format 指令添加选项来防止转义:
将变量 $ssl_client_s_dn 附加到 log_format 值中的适当位置,以便可以检查。如有必要,请研究 Nginx 日志模块以了解如何执行此操作https://nginx.org/en/docs/http/ngx_http_log_module.html
如果您是 Nginx 新手,可能需要解开很多东西...所以让我回顾一下要点,经过仔细考虑后,希望您可以正确地定制配置以适应确切的用例:
这个问题是特定于实现的,也就是说它没有通用的解决方案。这里没有一些简单的基本问题 - 这是一个由众多相关因素组成的复杂案例。
防止逃逸日志将帮助您更好地理解从证书中收到的值;请确保至少记录 $ssl_client_s_dn,直到您确定一切按预期工作。
如果最终用户以外的某个代理/服务器/服务将客户端证书发送到 Nginx,请记住这是主要依赖关系 - 它们如何构建证书编码决定了 Nginx 如何读取证书主题 DN。
请注意,仅凭这一点就可以解决上游破坏标头值的情况 - 如果您在标头中将 $ssl_client_s_dn 的值发送到 App/Uptream,请确保不要将其括在引号中,因为 Nginx 不需要对其进行转义,并且上游可能也不需要对其进行转义。如果已经这样做了,请将其更改为将其括在引号中。虽然我更喜欢不加引号的方法,但这个简单的更改可以“修复”App/Upstream 的问题:
我做了很多假设。您可能根本不会将证书主题 DN 转发到另一个资源,您可能只在 Nginx 中对其进行评估,您可能已经使用escape=none更新了 log_format ,可能的情况有很多……
在所有这些情况下,请记住每个组件都可以转义处理的值,或者仅在日志中转义。了解每个组件的配置方式以及它们如何处理字符串文字、UTF-8 多字节字符和/或数字常量是关键!
至少考虑以下因素:
a => 客户端证书的发送者,初始证书数据的格式
b => Nginx 通常使用它收到的确切证书主题 DN,但除非您覆盖此行为,否则它会默认在其自己的日志中转义值。
c => App/Upstream(如果您在 Header 中发送 $ssl_client_s_dn)可能会转义 Header 值,或者它可能保持不变并且仅在日志(如 Nginx)中转义它...
这些都是基本概念——如何为这个问题提供解决方案呢?
如果需要,有一种方法可以在发送到上游时修改 $ssl_client_s_dn 中设置的证书 DN 的值。
记录 $client_dn_fix 和 $ssl_client_s_dn 的值。
当查看日志时,您将能够看到 $ssl_client_s_dn 和 $client_dn_fix 中表达的证书主题 DN 的真实值,然后验证 Nginx 对它的看法。
通过查看日志来更好地了解内部操作后,您可以将“十六进制转义”或“原始”等值替换为类似“L=D'UNCONGÉ”(UTF-8 文字)或“L=D'UNCONG\xC3\x89”的字符串,以便上游收到“预期值”。
这里有很多可能的方法来实施这些概念,这取决于您真正需要什么。
编辑:OP已确认在检查 $ssl_client_s_dn 的值时日志中出现“L = D'UNCONG \ C3 \ 89”,因此以下映射最终有用,请注意这会将键/值减少到 2 个条目: