如何配置axios使用SSL证书?

123

我正在使用axios向API端点发出请求,但是遇到以下错误:Error:unable to verify the first certificate

看起来https模块无法验证服务器上使用的SSL证书,而axios使用了https模块。

当我用浏览器访问服务器时,证书是有效的,我可以查看/下载它。 我还可以通过https在浏览器上请求API。

我可以通过关闭验证来解决这个问题。 下面的代码可以工作:

const result = await axios.post(
    `https://${url}/login`,
    body,
    {
      httpsAgent: new https.Agent({
        rejectUnauthorized: false
      })
    }
  )

问题是,这样不会验证 SSL 证书,因此会导致安全漏洞。

我该如何配置 axios 来信任并正确验证证书?


请使用OkhttpClientFactory的本地实现,参考链接:https://dev59.com/PloV5IYBdhLWcg3wauKP#60116643 - Sumit
1
如果您正在寻找使用 @nestjs/axios 进行发布的方法,请参考此链接 - https://dev59.com/aWgv5IYBdhLWcg3wCsc8#72588991 - iravinandan
9个回答

141

虽然这个问题已经有些年头,但我也想为那些来到这里的人提供一些帮助。我不是专家,请向您当地的安全顾问等人咨询。

Axios是一个http(s)客户端,而http客户端通常匿名参与TLS。换句话说,服务器接受它们的连接而不识别谁在尝试连接。与之不同的是,双向TLS需要在握手完成之前,服务器和客户端都互相验证对方的身份。

因为互联网是一个危险的地方,我们想要保护我们的客户免于连接到欺骗性的公共终结点。我们通过确保客户在发送任何私人数据之前确定服务器的身份来实现这一点。

// DO NOT DO THIS IF SHARING PRIVATE DATA WITH SERVICE
const httpsAgent = new https.Agent({ rejectUnauthorized: false });

在StackOverflow上,关于任何语言中的https客户端连接故障,经常会发表这种答案(更加突出地得到了点赞)。更糟糕的是,它通常起作用,解决了开发人员的问题。然而,虽然他们肯定进入了门,但这是谁的门呢?由于他们选择不验证服务器的身份,他们可怜的客户端无法知道他们刚刚连接到公司内部网站的线路上是否有坏人监听。

如果服务有公共的SSL证书,则https.Agent通常不需要进一步配置,因为操作系统提供了一组公共受信任的CA证书。这通常是浏览器配置使用的同一组CA证书,这也是为什么默认的axios客户端可以轻松访问https://google.com的原因。

如果服务有一个私有的SSL证书(用于测试自签名或由您公司的私有CA签名以保护其内部机密),则必须配置https代理以信任用于签署服务器证书的私有CA:

const httpsAgent = new https.Agent({ ca: MY_CA_BUNDLE });

其中,MY_CA_BUNDLE是一个包含所需端点的服务器证书以及该证书完整证书链的 CA 证书的数组,格式为 .pem。必须包含链中所有证书直到信任根。


这些选项在哪里有文档记录?

HTTPS 是在 TLS/SSL 上的 HTTP 协议,在 Node.js 中作为单独的模块实现。

因此,传递给 https.Agent 的选项是从 tls.connect()tls.createSecureContext() 传递的选项合并而来。


3
在我下载证书及其链后,这最终为我解决了问题。之前我一直失败,因为我只下载了我想使用的服务的证书。对于像我一样苦苦挣扎的人:请确保您下载完整的链作为.pem文件。 - Jemi Salo
5
如何获取我自签名证书的完整链?我通过以下方式解决了这个问题:const httpsAgent = new https.Agent({ ca: fs.readFileSync(certPath) }); - Suge
3
这个在哪里有记录?我在 https.Agent 的文档中找不到关于“ca”选项的任何参考资料。 - user1974458
我真的很困惑。为什么我的浏览器能够信任私有CA,但在同一台机器上运行的node脚本中的axios却不能呢?! - Magnus
@Magnus 两者都必须配置为信任私有 CA。您没有提供足够的环境信息,以了解为什么您的浏览器信任 CA 而您的 Node.js 客户端不信任。请在另一个问题中发布,我们很乐意帮助。 - srquinn
显示剩余4条评论

37

使用SSL证书创建自定义代理:

const httpsAgent = new https.Agent({
  rejectUnauthorized: false, // (NOTE: this will disable client verification)
  cert: fs.readFileSync("./usercert.pem"),
  key: fs.readFileSync("./key.pem"),
  passphrase: "YYY"
})

axios.get(url, { httpsAgent })

// or

const instance = axios.create({ httpsAgent })
来自https://github.com/axios/axios/issues/284

13
rejectUnauthorized: false 禁用了客户端验证,这是 OP 最关注的问题之一。 - srquinn
1
因为这不是 OP 所要求的内容,所以被踩了。虽然这个答案仍然是正确的(除了 rejectUnauthorized),但它是关于客户端证书的,而 OP 想要验证服务器证书。 - pomeh
我认为关键参数有点奇怪,那个“key”是什么? - jambox
我们在React Native中从哪里导入https? - Shafqat Bari
将这个代码块放在客户端代码上安全吗? - undefined

32

对我来说,当我的应用程序处于开发模式下运行时,我直接在axios.defaults.options中禁用了rejectUnauthorized。这非常有效。请注意,仅在开发人员模式下使用此选项。

import https from 'https'
import axios from 'axios'
import config from '~/config'

/**
 * Axios default settings
 */
axios.defaults.baseURL = config.apiURL

/**
 * Disable only in development mode
 */
if (process.env.NODE_ENV === 'development') {
  const httpsAgent = new https.Agent({
    rejectUnauthorized: false,
  })
  axios.defaults.httpsAgent = httpsAgent
  // eslint-disable-next-line no-console
  console.log(process.env.NODE_ENV, `RejectUnauthorized is disabled.`)
}

2
需要注意的是,如果您的服务器位于“localhost”上,则这是安全的,但在通过互联网调用公共终端时不安全。 - srquinn
2
谢谢。这对我有用,但是我不得不将 axios.defaults.options 更改为 axios.defaults.httpsAgent - onoya
谢谢,我已经修复了。<o/ - Henrique Van Klaveren
在React Native中,你从哪里导入了https? - Shafqat Bari
@ShafqatBari,https模块是Node.js的原生模块。 - Henrique Van Klaveren

12

这些配置对我起作用了(在双向身份验证场景中)。

const httpsAgent = new https.Agent({
  ca: fs.readFileSync("./resource/bundle.crt"),        
  cert: fs.readFileSync("./resrouce/thirdparty.crt"),
  key: fs.readFileSync("./resource/key.pem"), 
})
注意:bundle.crt是从提供的证书(根证书、中间证书、最终入口证书)准备的。不幸的是,在这方面没有找到清晰的文档说明。

注意:bundle.crt是从提供的证书(根证书、中间证书、最终入口证书)准备的。不幸的是,在这方面没有找到清晰的文档说明。


1
要创建一个捆绑包,只需将提供的证书内容按相反顺序复制到一个文本文件中。在unix中,命令应该是:cat thirdparty.crt other.crt > bundle.crt,其中捆绑包的名称并不重要。 - rawplutonium
1
请告诉我其他.crt是什么?@rawplutonium - radiorz

10

这虽然很不规范,但在你的脚本顶部添加以下代码:

process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';

这基本上告诉Node不要检查SSL证书,在开发中遇到拒绝自签名证书时非常方便。

请不要在生产环境中使用此方法。


不是每次都有效。 - Pedro Paulo Amorim
不总是有效。 - undefined

4
这对我很有效:

import axios from 'axios'
import https from 'https'

const headers = {};

const httpsAgent = new https.Agent({
  ca: fs.readFileSync('./certs/cert.pem'),
  cert: fs.readFileSync('./certs/cert.pem'),
})

const data = await axios.get(url, { httpsAgent, headers })


在 React Native 中,你从哪里进行了 https 的导入? - Shafqat Bari
这是一个 Node.js 核心模块,在 React Native 中无法使用。 - EdwinN1337
如何从特定网站获取cert.pem文件? - zemunkh

2

注意:不要在生产环境中使用此代码,仅用于调试。

以下是我使用axios与nodejs + express的解决方案:

exports.test_ssl = async (req,res) => { 
       
    let cert_file = fs.readFileSync("./ssl/my_self_signed_certificate.crt")
    let ca_file = fs.readFileSync("./ssl/my_self_signed_certificate_ca.crt")
    const agent = new https.Agent({
        requestCert: true,
        rejectUnauthorized: true, // not for production
        cert: cert_file,
        ca: ca_file
    });
    const options = {
        url: `https://example.com/test`,
        method: "POST",
        httpsAgent: agent,
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/txt;charset=UTF-8'
        },
        data: {}
    };
    
    console.log(cert_file.toString())

    axios(options).then(response => {
        payload = response.data ;
        return res.status(200).send({"status":1});
    }).catch(err => {
        console.log(err);
        return false
    });

}

1
这个答案有 rejectUnauthorized: false,它关闭了服务器的身份验证检查,并暴露了你的客户端给中间人攻击。请参考被接受的答案,你不应该在生产工作负载中这样做。 - srquinn

1
const https = require('https');
const axios = require('axios')

const CA = "-----BEGIN CERTIFICATE-----$$$$$-----END CERTIFICATE-----"
const url = "bla"
const httpsAgent = new https.Agent({
  ca: CA
});

const response = await axios.get(url, { httpsAgent });

这是对我有效的工作方式。

你在 React Native 中从哪里导入了 https? - Shafqat Bari

-2

安装 https 包

npm i https

创建 httpsAgent
const https = require('https');
    const httpsAgent = new https.Agent({
      rejectUnauthorized: false,
    });

在axios调用中,在URL之后传递此代理

const { data } = await axios.get(url, { httpsAgent });

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