错误信息
self-signed certificate in certificate chain
也可能输出为
- code: 'SELF_SIGNED_CERT_IN_CHAIN'
- SELF_SIGNED_CERT_IN_CHAIN
Node.js 16 及更早版本(OpenSSL 1.1.1)写作“self signed certificate in certificate chain”。使用 fetch() 时,它作为“TypeError: fetch failed”的原因出现;npm 将其打印为“npm error code SELF_SIGNED_CERT_IN_CHAIN”。
Node.js tls / https / fetch — code SELF_SIGNED_CERT_IN_CHAIN (OpenSSL X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN, 19)
含义
服务器发送的证书链终止于一张自签名根证书,而它不在 Node.js 信任的根证书之列。在公司网络中,这是执行 TLS 检查的代理的典型特征:代理用自己的根证书为每个网站重新签名,并把该根证书放进证书链。Node.js 使用的是内置的 Mozilla 根证书列表,而不是 IT 部门安装代理根证书的操作系统证书存储。
原因
执行 TLS 检查的代理或杀毒软件
常见Zscaler、Netskope、Fortinet、Palo Alto 以及带 HTTPS 扫描功能的杀毒软件会出示自己的根证书。浏览器通过操作系统的证书存储信任它;Node.js 默认则不信任。
私有服务发送了自己的根证书
常见即使内部服务器在证书链中附带了公司根证书,客户端仍然需要信任这个根证书;仅仅发送它证明不了任何东西。
根证书已安装到操作系统中,但 Node.js 使用自己的列表
偶见Node.js 把 Mozilla 的根证书内置在二进制文件中,除非设置了 --use-system-ca 或 NODE_USE_SYSTEM_CA=1,否则会忽略系统证书存储。
修复方法
- 1.
查看证书链由谁签发
连接一个公共网站,逐级查看对端证书链。如果最顶端的证书以你的公司或某家安全厂商命名,就信任这个根证书;如果来源不明,在信任任何东西之前先调查清楚。
bash openssl s_client -connect registry.npmjs.org:443 -servername registry.npmjs.org -showcerts </dev/null 2>/dev/null \ | grep -E '^ *[0-9]+ s:|^ *i:' # The last line pair shows the root the chain ends in (s: and i: identical) - 2.
用 NODE_EXTRA_CA_CERTS 添加根证书
对于该进程的每个 TLS 连接,文件中的证书都会添加到内置根证书之中,包括 npm、yarn 和 fetch 的连接。在进程启动前设置该变量。
bash export NODE_EXTRA_CA_CERTS=/etc/ssl/private-ca/corp-proxy-root.pem node app.js - 3.
使用操作系统的证书存储
--use-system-ca 或 NODE_USE_SYSTEM_CA=1 让 Node.js 接受操作系统信任的根证书,这样 IT 部门部署的代理根证书无需复制文件即可生效。
bash # Managed machines already trust the proxy root in the OS store node --use-system-ca app.js NODE_USE_SYSTEM_CA=1 npm run build - 4.
临时诊断:NODE_TLS_REJECT_UNAUTHORIZED=0
为整个进程禁用证书验证。只在单次运行中用它确认原因;在经过拦截代理的网络中,它还会掩盖代理之外的任何攻击者。
bash # Temporary diagnostic only: confirms trust is the only problem, then remove it NODE_TLS_REJECT_UNAUTHORIZED=0 node app.js