$ssl_client_i_dn 不适合做信任链审计,因为它仅是客户端证书中声明的签发者dn,不反映ca是否可信、是否在信任列表、是否具备ca资格、是否有效或被吊销;信任链验证由openssl在tls握手阶段完成,而该变量仅在验证成功后可用,无法记录拒绝原因。

不能直接用 log_format 记录 $ssl_client_i_dn 来审计 CA 信任链合规性。
为什么 $ssl_client_i_dn 不适合做信任链审计
$ssl_client_i_dn 是客户端证书中 Issuer DN 字段的原始字符串(如 CN=Internal CA, O=MyOrg, C=CN),它只是“声称谁签了这张证书”,不表示该 CA 是否真实可信、是否在 Nginx 的信任列表中、是否具备 CA 资格(CA:TRUE)、是否在有效期内或是否被吊销。
信任链验证发生在 TLS 握手阶段,由 OpenSSL 根据 ssl_client_certificate 指定的 PEM 文件和 ssl_verify_depth 进行;而 $ssl_client_i_dn 在握手成功后才可用——也就是说,它只对已通过验证的请求有效,无法反映“为什么某次请求被拒”或“哪一级 CA 不被信任”。
真正能反映信任链状态的关键变量
Nginx 提供了几个与校验结果强相关的内置变量,它们才是审计信任链合规性的有效依据:
-
$ssl_client_verify:返回 SUCCESS、FAILED:reason 或 NONE。这是最核心的审计字段,直接表明验证动作的结果及失败原因(如FAILED:unable to get local issuer certificate或FAILED:certificate has expired) -
$ssl_client_cert:Base64 编码的完整客户端证书(PEM 格式)。可用于后端服务提取 SAN、DN、序列号等信息,结合证书吊销检查(OCSP/CRL)做深度审计 -
$ssl_client_s_dn和$ssl_client_i_dn:仅作上下文参考,不可替代验证逻辑。例如多个 CA 共存时,$ssl_client_i_dn可辅助识别使用的是 root 还是 intermediate,但不能证明其有效性
推荐的 log_format 审计配置
以下格式兼顾可读性与审计价值,适合作为安全日志接入 SIEM 或做离线分析:
log_format ssl_audit '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'cert_status="$ssl_client_verify" '
'issuer_dn="$ssl_client_i_dn" '
'subject_dn="$ssl_client_s_dn" '
'cert_serial="$ssl_client_serial" '
'protocol="$ssl_protocol" cipher="$ssl_cipher"';
access_log /var/log/nginx/ssl-audit.log ssl_audit;
注意:
- 必须开启 ssl_verify_client on 或 optional,否则 $ssl_client_verify 恒为 NONE
- 若需长期留存用于合规检查(如等保、PCI DSS),建议将 $ssl_client_cert 单独记录到加密日志通道,避免敏感证书内容明文落盘
补充信任链审计手段
仅靠 access_log 无法覆盖握手失败场景(如客户端未发证书、CA 不匹配、协议不支持)。要实现完整审计,还需:
- 启用 Nginx 错误日志 debug 级别(临时):
error_log /var/log/nginx/error.log debug;,查看SSL_do_handshake() failed及具体 OpenSSL 错误码 - 用
openssl s_client -connect host:443 -servername example.com -cert client.crt -key client.key -CAfile ca-bundle.pem离线复现并观察验证路径 - 定期检查
ssl_client_certificate指向的 PEM 文件是否包含全部中间 CA,且每张证书都满足Basic Constraints: CA:TRUE











