可通过解析nginx的$ssl_client_s_dn变量提取客户端证书标识,但需用map正则安全提取cn或serialnumber;高要求场景应使用auth_request交由后端解析原始证书。

可以通过解析 Nginx 中的 $ssl_client_s_dn 变量,提取客户端证书的唯一标识(如 CN、serialNumber 或自定义 OID 字段),但需注意该变量是 RFC 2253 格式的可分辨名称(DN)字符串,不是结构化数据,必须正确拆解才能可靠提取关键字段。
理解 $ssl_client_s_dn 的格式与限制
$ssl_client_s_dn 是 Nginx 在启用双向 TLS(mTLS)并成功验证客户端证书后自动设置的变量,其值为证书主题 DN 的标准字符串表示,例如:
"CN=alice@example.com,OU=Engineering,O=Acme Inc.,L=San Francisco,ST=CA,C=US"
它不包含证书序列号、指纹或公钥哈希,也不保证字段顺序一致(不同 CA 或工具生成的 DN 顺序可能不同)。特别要注意逗号(,)、反斜杠()、引号和等号在 DN 中可能被转义,直接按逗号分割易出错。
推荐方式:用 map 指令预提取 CN 或 serialNumber
Nginx 原生不支持正则捕获分组赋值,但可通过 map + 正则匹配安全提取常用字段。以下配置可提取 CN 值(兼容含空格、特殊字符和转义):
map $ssl_client_s_dn $client_cn {<br> default "";<br> ~*CN=([^,\]+|"[^"]*") $1;<br>}
说明:
• ~* 表示忽略大小写匹配
• CN=([^,\]+|"[^"]*") 匹配 CN 后紧接的非逗号非反斜杠内容,或双引号包裹的完整值(处理含逗号的 CN)
• 提取结果自动去除引号(Nginx map 不支持后处理,需应用层进一步清洗)
若需提取 serialNumber(常用于企业 PKI 唯一标识),可用类似模式:~*serialNumber=([^,\]+|"[^"]*") $1
更可靠方案:用 auth_request 模块交由后端解析
对唯一性要求高的场景(如金融、零信任接入),不建议仅依赖 DN 字段。应将原始证书透传给后端服务,由其解析 DER/X.509 结构,提取真正不可伪造的标识:
- 使用
$ssl_client_escaped_cert变量(base64 编码的 PEM 证书,已转义)或$ssl_client_raw_cert(需开启ssl_client_certificate和ssl_verify_depth) - 配置
auth_request转发请求到校验服务,例如:auth_request /cert-validate;location = /cert-validate { proxy_pass https://backend; } - 后端用 OpenSSL、Python 的 cryptography 库或 Java 的 Bouncy Castle 解析证书,读取
subject、serial_number、issuer及subjectKeyId等字段,组合生成业务唯一 ID
避免常见错误
• 不要用 split 或简单 substr 处理 $ssl_client_s_dn —— DN 中的逗号可能出现在引号内(如 CN="Smith, John")
• 不要假设 CN 总是第一个字段 —— 不同 CA 签发顺序不同
• 不要在日志中直接记录完整 $ssl_client_s_dn 用于审计 —— 它可能含敏感信息(如员工姓名),且不具唯一性
• 启用 mTLS 时务必设置 ssl_verify_client on 且提供可信 CA 证书链,否则 $ssl_client_s_dn 为空或不可信











