87

我有代码:

var r = require('request');
r({
  method: 'POST',
  url: 'https://api.dropbox.com'},
  function() { console.log(arguments)  } )

当我使用 Node 0.9.4 在桌面上运行它时,我在控制台中得到了这个:

{ '0': [Error: Hostname/IP doesn't match certificate's altnames] }

当我在使用 Node 0.6.12 的上网本上运行它时,一切正常,没有错误(302 响应 - 我认为它是正确的)。

Node.js 主机名/IP 与证书替代名称不匹配的问题中,Rojuinex 写道:“是的,浏览器问题......对不起”。“浏览器问题”是什么意思?

UPD。在 Node v0.8 上回滚后,此问题已解决

4

12 回答 12

101

从 0.9.2(包括 0.10.x)开始,node.js 现在默认验证证书。这就是为什么当你升级到 node.js 0.8 之后你会看到它变得更加严格。(HT:https ://github.com/mscdex/node-imap/issues/181#issuecomment-14781480 )

{rejectUnauthorized:false}您可以使用该选项避免这种情况,但是这会带来严重的安全隐患。您发送给对等方的任何内容仍将被加密,但发起中间人攻击变得更加容易,即您的数据将被加密给对等方,但对等方本身并不是您认为的服务器!

最好先诊断证书未授权的原因,然后看看是否可以修复。

于 2013-05-01T00:41:44.523 回答
42

一个稍微更新的答案(因为我在不同的情况下遇到了这个问题。)

当您使用 SSL 连接到服务器时,服务器所做的第一件事就是提供一个证书,上面写着“我是 api.dropbox.com”。证书有一个“主题”,该主题有一个“CN”(“通用名称”的缩写)。证书也可以有一个或多个“subjectAltNames”。当 node.js 连接到服务器时,node.js 会获取此证书,然后验证它认为它正在连接的域名 (api.dropbox.com) 是否与主题的 CN 或其中一个替代名称匹配。请注意,在节点 0.10.x 中,如果您使用 IP 连接,则 IP 地址必须在 altnames 中 - node.js 不会尝试根据 CN 验证 IP。

rejectUnauthorized标志设置为 false 将绕过此检查,但首先,如果服务器为您提供的凭据与您预期的不同,则会发生一些可疑的事情,其次这也会绕过其他检查 - 如果您这样做不是一个好主意'正在通过 Internet 连接。

如果你使用 node >= 0.11.x,你还可以为checkServerIdentity: function(host, cert)tls 模块指定一个函数,如果你想允许连接,它应该返回,undefined否则抛出异常(虽然我不知道是否request会通过代理这个标志tls 为你。)声明这样的函数并console.log(host, cert);弄清楚到底发生了什么可能很方便。

于 2015-01-21T15:56:09.487 回答
35

修复包http-proxy的问题

1) HTTP (localhost) 访问 HTTPS 要解决此问题,请将 changeOrigin 设置为 true。

const proxy = httpProxy.createProxyServer();

proxy.web(req, res, {
  changeOrigin: true,
  target: https://example.com:3000,
});

2) HTTPS 访问 HTTPS你应该包括 SSL 证书

httpProxy.createServer({
  ssl: {
    key: fs.readFileSync('valid-ssl-key.pem', 'utf8'),
    cert: fs.readFileSync('valid-ssl-cert.pem', 'utf8')
  },
  target: 'https://example.com:3000',
  secure: true
}).listen(443);
于 2017-08-08T22:50:02.103 回答
15

我在使用请求模块从其他地方代理 POST 请求时遇到了同样的问题,这是因为我将主机属性留在了标头中(我正在从原始请求中复制标头)。

于 2015-11-18T03:58:02.077 回答
14

在其他情况下解决此问题的另一种方法是NODE_TLS_REJECT_UNAUTHORIZED=0用作环境变量

NODE_TLS_REJECT_UNAUTHORIZED=0 node server.js

警告:这在安全方面是个坏主意

于 2018-04-05T07:30:18.273 回答
13

我知道这是旧的,但对于其他人来说:

从主机名中删除 https:// 并添加端口 443。

{
  method: 'POST',
  hostname: 'api.dropbox.com',
  port: 443
}
于 2015-02-10T13:14:01.517 回答
8

在验证证书是由已知的证书颁发机构 (CA) 颁发后,将检查主题备用名称或通用名称,以验证主机名是否匹配。这是在checkServerIdentity 函数中。如果证书具有主题备用名称并且未列出主机名,您将看到描述的错误消息:

主机名/IP 与证书的替代名称不匹配

如果您拥有用于生成您正在使用的证书的 CA 证书(通常是使用自签名证书时的情况),则可以提供

var r = require('request');

var opts = {
    method: "POST",
    ca: fs.readFileSync("ca.cer")
};

r('https://api.dropbox.com', opts, function (error, response, body) {
    // do something
});

这将验证证书是否由提供的 CA 颁发,但仍将执行主机名验证。如果证书包含主题备用名称中的主机名,只需提供 CA 就足够了。如果没有,并且您还想跳过主机名验证,您可以传递一个 noop 函数checkServerIdentity

var r = require('request');

var opts = {
    method: "POST",
    ca: fs.readFileSync("ca.cer"),
    agentOptions: { checkServerIdentity: function() {} }
};

r('https://api.dropbox.com', opts, function (error, response, body) {
    // do something
});
于 2018-07-06T01:10:01.953 回答
2

localhost如果我们使用目标地址(hosthostname在 node.js 上)测试我们的客户端请求并且我们的服务器公用名CN = localhost在服务器证书中,我们就没有这个问题。但即使我们更改IP 或任何其他 IP localhost127.0.0.1我们也会Hostname/IP doesn't match certificate's altnames在 node.js 或SSL handshake failedQT 上得到错误。

在我的客户端请求中,我的服务器证书也有同样的问题。为了在我的客户端 node.js 应用程序上解决它,我需要在我subjectAltName的应用程序上server_extension添加以下值:

[ server_extension ]
       .
       .
       .

subjectAltName          = @alt_names_server

[alt_names_server]
IP.1 = x.x.x.x

然后我-extension在创建和签署证书时使用。

例子:

我而言,我首先导出发行者的配置文件,因为该文件包含server_extension

export OPENSSL_CONF=intermed-ca.cnf

所以我创建并签署了我的服务器证书:

openssl ca \
    -in server.req.pem \
    -out server.cert.pem \
    -extensions server_extension \
    -startdate `date +%y%m%d000000Z -u -d -2day` \
    -enddate `date +%y%m%d000000Z -u -d +2years+1day`   

它在基于 node.js 的带有 https 请求的客户端上运行良好,但在我们定义时它不适用于基于 QT QSsl 的客户端sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyPeer),除非我们使用QSslSocket::VerifyNone它不起作用。如果我们使用VerifyNone它将使我们的应用程序不检查对等证书,因此它将接受任何证书。因此,要解决它,我需要更改其证书上的服务器公用名,并将其值替换为我的服务器运行的 IP 地址。

例如:

CN = 127.0.0.1

于 2018-03-01T20:44:57.990 回答
2

从 AWS 中的 Lambda 函数流式传输到 ElasticSearch 时,我得到了这个。把我的头撞在墙上试图弄清楚。最后,在设置request.headers['Host']https://为 ES 添加到域中时 - 将其更改为[es-domain-name].eu-west-1.es.amazonaws.com(不带 https://)立即工作。下面是我用来让它工作的代码,希望能避免其他人把头撞到墙上......

import path from 'path';
import AWS from 'aws-sdk';

const { region, esEndpoint } = process.env;
const endpoint = new AWS.Endpoint(esEndpoint);
const httpClient = new AWS.HttpClient();
const credentials = new AWS.EnvironmentCredentials('AWS');

/**
 * Sends a request to Elasticsearch
 *
 * @param {string} httpMethod - The HTTP method, e.g. 'GET', 'PUT', 'DELETE', etc
 * @param {string} requestPath - The HTTP path (relative to the Elasticsearch domain), e.g. '.kibana'
 * @param {string} [payload] - An optional JavaScript object that will be serialized to the HTTP request body
 * @returns {Promise} Promise - object with the result of the HTTP response
 */
export function sendRequest ({ httpMethod, requestPath, payload }) {
    const request = new AWS.HttpRequest(endpoint, region);

    request.method = httpMethod;
    request.path = path.join(request.path, requestPath);
    request.body = payload;
    request.headers['Content-Type'] = 'application/json';
    request.headers['Host'] = '[es-domain-name].eu-west-1.es.amazonaws.com';
    request.headers['Content-Length'] = Buffer.byteLength(request.body);
    const signer = new AWS.Signers.V4(request, 'es');
    signer.addAuthorization(credentials, new Date());

    return new Promise((resolve, reject) => {
        httpClient.handleRequest(
            request,
            null,
            response => {
                const { statusCode, statusMessage, headers } = response;
                let body = '';
                response.on('data', chunk => {
                    body += chunk;
                });
                response.on('end', () => {
                    const data = {
                        statusCode,
                        statusMessage,
                        headers
                    };
                    if (body) {
                        data.body = JSON.parse(body);
                    }
                    resolve(data);
                });
            },
            err => {
                reject(err);
            }
        );
    });
}
于 2020-08-17T13:33:43.400 回答
0

如果您要信任子域,例如 aaa.localhost,请不要这样做mkcert localhost *.localhost 127.0.0.1,因为某些浏览器不接受通配符子域,这将不起作用。

也许试试mkcert localhost aaa.localhost 127.0.0.1

于 2020-01-08T05:02:46.673 回答
0

对于在 Node.js 应用程序中使用Fetch API的开发人员,这就是我使用rejectUnauthorized使其工作的方式。

请记住,使用rejectUnauthorized是危险的,因为它会为您带来潜在的安全风险,因为它会绕过有问题的证书。

const fetch = require("node-fetch");
const https = require('https');

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

async function getData() {
  const resp = await fetch(
    "https://myexampleapi.com/endpoint",
    {
      agent: httpsAgent,
    },
  )
  const data = await resp.json()
  return data
}
于 2020-12-01T17:24:26.490 回答
0

这在使用 nodemailer 时对我有用:

var transporter = nodemailer.createTransport({
            host: 'mail.site.com',
            port: 25,
            secure: false,
            auth: {
                user: 'info@site.com',
                pass: 'YOUR_PASSWORD'
            },
            tls: {
                rejectUnauthorized: false
            }
        });
于 2021-09-23T15:33:30.443 回答