我有代码:
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] }
当我在Netbook上使用Node 0.6.12运行它时,它都可以正常工作(302响应 - 我认为是正确的。)
在问题Node.js hostname/IP doesnt match certificates altnames中,Rojuinex写道:“是的,浏览器问题......对不起”。 “浏览器问题”是什么意思?
UPD。在回滚节点v0.8
后解决了此问题答案 0 :(得分:58)
由于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}
选项来避免此问题,但这会产生严重的安全隐患。您发送给对等方的任何内容仍然会被加密,但它会更容易 更容易发起中间人攻击,即您的数据将被加密到对等方但对方本身不是你认为它的服务器!
最好先诊断证书未授权的原因,看看是否可以修复证书。
答案 1 :(得分:21)
稍微更新的答案(因为我在不同的情况下遇到了这个问题。)
当您使用SSL连接到服务器时,服务器所做的第一件事就是出现一个证明“我是api.dropbox.com”的证书。证书具有“主题”,主题具有“CN”(“通用名称”的缩写)。证书也可以具有一个或多个“subjectAltNames”。当node.js连接到服务器时,node.js获取此证书,然后验证它认为它连接到的域名(api.dropbox.com)是否与主题的CN或其中一个altnames匹配。请注意,在节点0.10.x中,如果使用IP进行连接,则IP地址必须位于altnames中 - node.js不会尝试针对CN验证IP。
将rejectUnauthorized
标志设置为false将绕过此检查,但首先如果服务器提供的凭据与您预期的不同,则会发生一些可疑的事情,其次这也会绕过其他检查 - 如果你通过互联网连接,这不是一个好主意。
如果您使用节点> = 0.11.x,您还可以为tls模块指定checkServerIdentity: function(host, cert)
函数,如果您想允许连接并抛出异常,则应返回undefined
否则(虽然我不知道request
是否会将此标志代理到你的tls。)声明这样的函数和console.log(host, cert);
来弄清楚究竟发生了什么是很方便的。
答案 2 :(得分:15)
如果您使用的是http-proxy软件包,则当您的服务器是HTTP(例如localhost)且目标是HTTPS时会发生这种情况。要解决此问题,请将changeOrigin设置为true。
const proxy = httpProxy.createProxyServer();
proxy.web(req, res, {
changeOrigin: true,
target: https://example.com:3000,
});
如果您的服务器是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);
答案 3 :(得分:8)
我知道这是旧的,但对于其他任何人看:
从主机名中删除https://并改为添加端口443.
{
method: 'POST',
hostname: 'api.dropbox.com',
port: 443
}
答案 4 :(得分:4)
在其他情况下解决此问题的另一种方法是使用NODE_TLS_REJECT_UNAUTHORIZED=0
作为环境变量
NODE_TLS_REJECT_UNAUTHORIZED=0 node server.js
答案 5 :(得分:3)
我使用请求模块从其他地方代理POST请求时遇到了同样的问题,这是因为我将主机属性留在了标题中(我正在复制原始请求中的标题)。
答案 6 :(得分:2)
在验证证书是由已知的证书颁发机构(CA)颁发之后,将检查“使用者备用名称”或“公用名”,以验证主机名是否匹配。这是在checkServerIdentity function中。如果证书具有“使用者备用名称”,但未列出主机名,您将看到描述的错误消息:
主机名/ 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就足够了。如果不是,并且您还想跳过主机名验证,则可以为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
});
答案 7 :(得分:1)
如果我们使用localhost
目标地址(node.js上的host
或hostname
)测试我们的客户端请求,并且我们的服务器通用名称是,那么我们就不会遇到此问题服务器证书中的CN = localhost
。但即使我们更改localhost
127.0.0.1
或任何其他IP,我们也会在node.js上获得错误Hostname/IP doesn't match certificate's altnames
或在QT上收到SSL handshake failed
。
我在客户端请求中遇到了与服务器证书相同的问题。要在我的客户端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`
在基于具有https请求的node.js的客户端上运行正常但在我们定义
sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyPeer)
时它不能与基于QT QSsl的客户端一起使用,除非我们使用QSslSocket::VerifyNone
它赢了# 39;工作。如果我们使用VerifyNone
,它将使我们的应用不会检查对等证书,以便它接受任何证书。因此,要解决此问题,我需要在其证书上更改我的服务器公用名,并替换其运行我的服务器的IP地址的值。
例如:
CN = 127.0.0.1
答案 8 :(得分:0)
如果您要信任一个子域,例如aaa.localhost,
请不要像mkcert localhost *.localhost 127.0.0.1
那样做,因为某些浏览器不接受通配符子域,所以此操作将无效。
也许尝试mkcert localhost aaa.localhost 127.0.0.1
。
答案 9 :(得分:0)
当从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);
}
);
});
}
答案 10 :(得分: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
}