Nodejs https请求 UNABLE_TO_GET_ISSUER_CERT_LOCALLY

7

操作系统:debian sid

Node.js版本:v0.10.38

我向一个需要身份验证的私有服务发送了一个请求:

var https = require('https');

var options = {
    host: 'private.service.com',
    path: '/accounts/' + '123323' + '/orders',
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': 0,
        'Authorization': 'Bearer ' + 'asdsdgcvxcvxcv'
    }
};

var request = https.request(options, function (res) {
    console.log(res);
});

当我运行这个脚本时,Node会抛出以下错误:
events.js:72
    throw er; // Unhandled 'error' event
          ^
Error: UNABLE_TO_GET_ISSUER_CERT_LOCALLY
    at SecurePair.<anonymous> (tls.js:1381:32)
    at SecurePair.emit (events.js:92:17)
    at SecurePair.maybeInitFinished (tls.js:980:10)
    at CleartextStream.read [as _read] (tls.js:472:13)
    at CleartextStream.Readable.read (_stream_readable.js:341:10)
    at EncryptedStream.write [as _write] (tls.js:369:25)
    at doWrite (_stream_writable.js:226:10)
    at writeOrBuffer (_stream_writable.js:216:5)
    at EncryptedStream.Writable.write (_stream_writable.js:183:11)
    at write (_stream_readable.js:602:24)

这个完全相同的脚本之前运行了好几个月,并且我确定认证是正确的。今天是我第一次遇到这种情况。

导致这个错误的可能原因是什么?

5个回答

17

经过一些研究,我发现这是我试图向其发出https请求的服务器的问题。Node https在private.service服务器上找不到ssl ISSUER_CERT,因此会抛出异常。

我使用的解决方案是,由于我确定可以信任该服务器,所以添加

            rejectUnauthorized: false

通过给https请求的选项添加参数,Node将不会在证书问题出现时抛出异常。

不过,这个解决方案仅在您知道可以信任请求主机的情况下才是有效的,否则这可能不是最好的解决方案。


2
请看我的回答,为什么这样做虽然足以“让它工作”,但会危及您的客户端应用程序受到中间人攻击的风险。 - zakjan
谢谢您设置 rejectUnauthorized: false。它起作用了。虽然 zakjan 在他的答案中提到了它,但是您指出了它应该添加在哪里。 - Mohamed Iqzas
2
不要这样做。你正在使用SSL进行安全保护,然后明确地将其关闭,那么一开始为什么要费心使用SSL呢?由于客户端无法确定服务器的身份,因此你会面临中间人攻击的风险。 - srquinn
在测试或非生产环境中禁用此功能可能是可以的。 - samlandfried

13

SSL有其存在的原因。除了其他特性外,它还可以验证您是否真正与私人服务主机名为private.service.com的服务器进行通信。否则,您的客户端软件可能会受到中间人攻击的欺骗。

首先,当任何人遇到此问题时,应更新系统根SSL证书。在Debian中,它们包含在ca-certificates apt-get软件包中。

如果这样做没有帮助,则服务器可能使用的是发行方证书,该证书未被默认全球PKI基础结构信任。在这种情况下,客户端应将证书公钥签名与预共享值进行比较。这就是所谓的“证书固定”。

具体到您的错误,如果之前能够正常工作,则可能是服务器证书已过期。服务器应该更新它。作为临时解决方法,您可以通过rejectUnauthorized选项关闭PKI验证。但是,您应该将其与固定方法一起使用。在NodeJS中,您可以从res.socket.getPeerCertificate().fingerprint获取服务器证书指纹。


我会尝试你的建议,然后告诉你结果,感谢你提供更深入的解释。 - pietrovismara

4

经过研究,问题得到解决:

npm set strict-ssl=false

希望能对你有所帮助。

是的,它在我这里运行良好,但要使用此命令进行更改,您必须在超级用户会话中运行它-->sudo npm set strict-ssl=false,非常感谢。 - Ahmed Mahmoud
4
你正在使用SSL进行安全连接,但却明确关闭它,这样一来,一开始使用SSL又有什么意义呢?你会因为客户端无法确定服务器的身份而容易受到中间人攻击。 - srquinn

2

根据这个GitHub问题,请使用以下环境变量:

Original Answer翻译成“最初的回答”。

export NODE_EXTRA_CA_CERTS=/path/to/certfile.crt

在此之后,您可以运行任何npm命令,nodejs将信任您的额外证书文件。

.crt文件是一个包含前缀行、base64编码证书和后缀行的文件。

如果您没有根CA证书,请使用openssl命令从您的URL生成证书文件:

(请注意:本翻译内容仅供参考,具体操作请谨慎。)

openssl s_client \
    -showcerts \
    -servername <host> \
    -connect <host>:443 2>/dev/null </dev/null | \
  openssl x509

<host>是URL的主机名。例如,使用google.com作为主机名,输出看起来像这样:

最初的回答:

<host>指url的域名部分,例如使用google.com作为主机名,输出结果类似于:

-----BEGIN CERTIFICATE-----
MIIIIDCCBwigAwIBAgIQI/1aCHwDN94QFUhXJeIwODANBgkqhkiG9w0BAQsFADBU
(42 more lines)
E+Hv1bJSbOMSNCDzqRM3JUzvM6Y=
-----END CERTIFICATE-----

将该文件另存为.crt 文件,将其路径添加到NODE_EXTRA_CA_CERTS 环境变量中,然后你就可以了解决问题了。"Original Answer"的翻译是"最初的回答"。

-3

当我在尝试在Ubuntu 16上安装npm时,遇到了相同的错误。 以下命令对我有用。

npm config set registry http://registry.npmjs.org/

这是不好的,因为它没有使用SSL。类似于执行以下操作:npm set strict-ssl=false。除非您完全不关心安全性,否则不要这样做。 - ADJenks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接