确定ajax调用是否由于响应不安全或连接被拒绝而失败


114

我一直在做很多研究,找不到解决这个问题的方法。我正在尝试执行从https服务器到运行带有自定义自签名证书的码头的locahost https服务器的jQuery ajax调用。我的问题是我无法确定响应是连接被拒绝还是响应不安全(由于缺少证书接受)。有没有办法确定两种情况之间的差异?的responseText,并且statusCode总是在这两种情况下是相同的,即使在Chrome控制台我可以看到一个区别:

net::ERR_INSECURE_RESPONSE
net::ERR_CONNECTION_REFUSED

responseTextstatusCode对于这两种情况,始终为“”,始终为“ 0”。

我的问题是,如何确定jQuery ajax调用是由于ERR_INSECURE_RESPONSE还是由于失败ERR_CONNECTION_REFUSED

一旦证书被接受,一切正常,但是我想知道本地主机服务器是否已关闭,或者它已启动并正在运行,但是证书尚未被接受。

$.ajax({
    type: 'GET',
    url: "https://localhost/custom/server/",
    dataType: "json",
    async: true,
    success: function (response) {
        //do something
    },
    error: function (xhr, textStatus, errorThrown) {
        console.log(xhr, textStatus, errorThrown); //always the same for refused and insecure responses.
    }
});

在此处输入图片说明

即使手动执行请求,我也会得到相同的结果:

var request = new XMLHttpRequest();
request.open('GET', "https://localhost/custom/server/", true);
request.onload = function () {
    console.log(request.responseText);
};
request.onerror = function () {
    console.log(request.responseText);
};
request.send();

6
我将发布我的代码,但这不是JavaScript代码错误。请仔细阅读我的问题。
taxicala 2015年

1
其他两个错误回调参数是否提供其他任何见解?function (xhr, status, msg) {...我怀疑他们会,但是值得尝试。
凯文B

2
否。从一台服务器到另一台服务器。码头服务器(在locahost中运行)已经正确设置了CORS标头,因为一旦我接受了证书,一切都会按预期进行。我想确定是否必须接受证书或码头是否停机。
taxicala 2015年

5
浏览器缺乏信息完全有可能是故意的。简单地,“错误”将为拟议的黑客提供一些信息。“此系统在端口等上监听某些内容。”
Katana314 2015年

1
这不是我想要的东西,而是我所设计和开发的东西所需要的东西。服务器端不是一种选择。
taxicala 2015年

Answers:


67

无法将其与最新的Web浏览器区分开。

W3C规格:

以下步骤描述了用户代理对于简单的跨域请求必须执行的操作

应用发出请求的步骤,并在发出请求时遵守下面的请求规则。

如果未设置手动重定向标志,并且响应的HTTP状态代码为301、302、303、307或308,请 执行重定向步骤。

如果最终用户取消请求,请 应用中止步骤。

如果存在网络错误如果出现 DNS错误,TLS协商失败或其他类型的网络错误,请应用网络错误步骤。不要请求任何类型的最终用户交互。

注意:这不包括指示某种错误类型的HTTP响应,例如HTTP状态代码410。

否则,请 执行资源共享检查。如果返回失败,则应用网络错误步骤。否则,如果返回通过,则终止此算法并将跨域请求状态设置为成功。实际不终止请求。

如您所见,网络错误不包括包含错误的HTTP响应,这就是为什么您总是得到0作为状态代码和“”作为错误的原因。

资源


注意:以下示例是使用Google Chrome版本43.0.2357.130并针对我创建的用于模拟OP one的环境制作的。设置代码位于答案的底部。


我虽然是一个办法要解决这将是使通过HTTP的二次请求,而不是HTTPS作为这个答案,但我记得那是不可能的,因为浏览器的较新版本阻止混合内容。

这意味着,如果您使用的是HTTPS,则Web浏览器将不允许通过HTTP发出请求,反之亦然。

几年前就一直这样,但是较旧的Web浏览器版本(如Mozilla Firefox)低于其版本23。

证据:

从HTTPS usign Web Broser控制台发出HTTP请求

var request = new XMLHttpRequest();
request.open('GET', "http://localhost:8001", true);
request.onload = function () {
    console.log(request.responseText);
};
request.onerror = function () {
    console.log(request.responseText);
};
request.send();

将导致以下错误:

混合内容:“ https:// localhost:8000 / ”上的页面已通过HTTPS加载,但请求了不安全的XMLHttpRequest端点“ http:// localhost:8001 / ”。该请求已被阻止;内容必须通过HTTPS提供。

如果您尝试通过其他方式(例如添加iframe)来执行此操作,则浏览器控制台中会出现相同的错误。

<iframe src="http://localhost:8001"></iframe>

还使用Socket连接作为答案,我很确定结果是相同/相似的,但是我已经尝试了。

尝试使用HTTPS打开从Web Broswer到非安全套接字端点的套接字连接,将导致混合内容错误。

new WebSocket("ws://localhost:8001", "protocolOne");

1)混合内容:“ https:// localhost:8000 / ”上的页面已通过HTTPS加载,但是试图连接到不安全的WebSocket端点“ ws:// localhost:8001 /”。该请求已被阻止;该端点必须在WSS上可用。

2)未捕获的DOMException:无法构造“ WebSocket”:可能无法从通过HTTPS加载的页面启动不安全的WebSocket连接。

然后,我也尝试连接到wss端点,请参阅是否可以阅读有关网络连接错误的一些信息:

var exampleSocket = new WebSocket("wss://localhost:8001", "protocolOne");
exampleSocket.onerror = function(e) {
    console.log(e);
}

在服务器关闭的情况下执行上述代码段会导致:

WebSocket与'wss:// localhost:8001 /'的连接失败:连接建立错误:net :: ERR_CONNECTION_REFUSED

在服务器开启的情况下执行上述代码段

WebSocket与“ wss:// localhost:8001 /”的连接失败:WebSocket打开握手被取消

但是同样,“ onerror函数”输出到控制台的错误没有任何提示来区分一个错误和另一个错误。


使用代理作为此答案建议可能有效,但前提是“目标”服务器具有公共访问权限。

这里不是这种情况,因此在这种情况下尝试实现代理将导致我们遇到相同的问题。

创建Node.js HTTPS服务器的代码

我创建了两个使用自签名证书的Nodejs HTTPS服务器:

targetServer.js:

var https = require('https');
var fs = require('fs');

var options = {
    key: fs.readFileSync('./certs2/key.pem'),
    cert: fs.readFileSync('./certs2/key-cert.pem')
};

https.createServer(options, function (req, res) {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
    res.writeHead(200);
    res.end("hello world\n");
}).listen(8001);

applicationServer.js:

var https = require('https');
var fs = require('fs');

var options = {
    key: fs.readFileSync('./certs/key.pem'),
    cert: fs.readFileSync('./certs/key-cert.pem')
};

https.createServer(options, function (req, res) {
    res.writeHead(200);
    res.end("hello world\n");
}).listen(8000);

为了使其正常工作,您需要安装Nodejs,需要为每个服务器生成单独的证书,并将其分别存储在certs和certs2文件夹中。

运行它只是执行node applicationServer.jsnode targetServer.js在终端(ubuntu的例子)。


6
这是迄今为止最完整的答案。它表明您实际上已经做了一些研究工作和测试。我看到您已经尝试了所有可能的方法来执行此操作,并且所有场景都得到了很好的解释。您还提供了示例代码来快速设置测试环境!真的很好!感谢您的见解!
taxicala

很好的答案!但是,我想指出的是,如果自签名证书来自不同的服务器,它们仍然会在浏览器中产生错误。@ecarrizo
FabricioG

如果我们正在谈论网络错误,可能您可以在控制台中手动看到错误。但是您无法在安全的环境中通过代码访问它。
ecarrizo

32

截至目前:尚无办法区分浏览器之间的此事件。由于浏览器不提供事件供开发人员访问。(2015年7月)

该答案仅旨在提供一种可能的,顽固的,不完整的解决方案的想法。


免责声明:此答案不完整,因为它不能完全解决OP的问题(由于跨域策略)。但是,此想法本身确实具有一些优点,可以通过以下方式进一步扩展:@artur grzesiak 在这里,使用代理和Ajax。


本人经过大量研究后,似乎没有任何形式的错误检查来检查连接被拒绝和不安全的响应之间的差异,至少就JavaScript提供了两者之间的响应而言。

我的研究普遍认为SSL证书是由浏览器处理的,因此,在用户接受自签名证书之前,浏览器会锁定所有请求,包括对状态码的请求。浏览器可以(如果编码为)发送自己的状态代码以响应不安全的响应,但这实际上并没有任何帮助,即使那样,您在浏览器兼容性方面也会遇到问题(chrome / firefox / IE具有不同的标准)。 .. 再来一次)

由于您最初的问题是在启动服务器和获得不可接受的证书之间检查服务器的状态,因此您不能像这样发出标准的HTTP请求吗?

isUp = false;
isAccepted = false;

var isUpRequest = new XMLHttpRequest();
isUpRequest.open('GET', "http://localhost/custom/server/", true); //note non-ssl port
isUpRequest.onload = function() {
    isUp = true;
    var isAcceptedRequest = new XMLHttpRequest();
    isAcceptedRequest.open('GET', "https://localhost/custom/server/", true); //note ssl port
    isAcceptedRequest.onload = function() {
        console.log("Server is up and certificate accepted");
        isAccepted = true;
    }
    isAcceptedRequest.onerror = function() {
        console.log("Server is up and certificate is not accepted");
    }
    isAcceptedRequest.send();
};
isUpRequest.onerror = function() {
    console.log("Server is down");
};
isUpRequest.send();

当然,这确实需要额外的请求来验证服务器连接性,但是它应该通过消除过程来完成工作。不过,仍然觉得自己很笨拙,我也不喜欢加倍要求。


7
我已经阅读了整个答案,但我不太认为这行得通。我的两台服务器都运行HTTPS,这意味着当我向HTTP服务器发出请求时,浏览器会立即将其取消,因为您无法从HTTPS服务器向HTTP服务器发出ajax请求
taxicala 2015年

11

@Schultzie的答案非常接近,但很明显http-通常- https在浏览器环境中无法使用。

但是,您可以做的是使用中间服务器(代理)代表您发出请求。代理应允许转发http来自https源的请求或加载自签名源的内容

在您的情况下,拥有自己的具有适当证书的服务器可能是一个过大的选择-因为您可以使用此设置而不是具有自签名证书的计算机-但是那里有很多匿名开放代理服务

因此,我想到两种方法:

  1. ajax请求 -在这种情况下,代理必须使用适当的CORS设置
  2. 使用iframe-您可以通过代理在iframe中加载脚本(可能用html包装)。脚本加载后,就会向发送一条消息.parentWindow。如果您的窗口收到消息,则可以确定服务器正在运行(或更准确地说,是之前运行了不到一秒钟)。

如果您只对本地环境感兴趣,则可以尝试使用chrome和--disable-web-securityflag 运行。


另一个建议:您是否尝试过以编程方式加载图像以了解是否存在更多信息?


1

退房 jQuery.ajaxError() 引用自:jQuery AJAX错误处理(HTTP状态代码) 它捕获全局Ajax错误,您可以通过HTTP或HTTPS以多种方式处理该错误:

if (jqXHR.status == 501) {
//insecure response
} else if (jqXHR.status == 102) {
//connection refused
}

这与我执行ajax的方式相同,但是将错误响应集中在一个地方。
taxicala

1
关键是在进行ajax调用时未加载证书,因为这似乎是问题所在。任何方式都可以找到答案:)
Varshaan 2015年

2
不,问题是我想确定必须接受证书与知道服务器是否关闭之间的区别。
taxicala 2015年

1
当拒绝连接和不信任证书时,我都得到相同的jqXHR.status = 0(和jqXHR.readyState = 0),而不是此答案中描述的qXHR.status == 102或501。
anre

1

不幸的是,当前的浏览器XHR API并未为浏览器何时由于“不安全的响应”而拒绝连接以及何时不信任网站的HTTP / SSL证书提供明确的指示。

但是有解决此问题的方法。

我想确定浏览器何时不信任HTTP / SSL证书的一种解决方案是,首先检测是否发生XHR错误(error()例如,使用jQuery 回调),然后检查XHR调用是否针对'https ://'URL,然后检查XHR readyState是否为0,这意味着XHR连接甚至都没有打开(浏览器不喜欢证书时会发生这种情况)。

这是我执行此操作的代码:https : //github.com/maratbn/RainbowPayPress/blob/e9e9472a36ced747a0f9e5ca9fa7d96959aeaf8a/rainbowpaypress/js/le_requirejs/public/model_info__transaction_details.js#L88


1

我认为目前尚无检测这些错误消息的方法,但是可以采取的措施是在应用程序服务器前使用像nginx这样的服务器,这样,如果应用程序服务器出现故障,您将受到严重影响。来自nginx的网关错误,502其中包含可以在JS中检测到的状态代码。否则,如果证书无效,您仍然会收到与相同的通用错误statusCode = 0

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.