确定是否Ajax调用由于不安全的响应或拒绝连接失败 [英] Determine if ajax call failed due to insecure response or connection refused

查看:7663
本文介绍了确定是否Ajax调用由于不安全的响应或拒绝连接失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经做了很多研究,但没有找到一种方法来处理这​​个问题。我试图执行从HTTPS服务器一个jQuery AJAX调用一个locahost HTTPS服务器上运行的码头有一个自定义的自签名证书。我的问题是,我不能确定响应是否拒绝连接或不安全的响应(由于缺乏认证认可)。有没有一种方法来确定这两种方案之间的区别?该的responseText 状态code 总是在这两种情况下是相同的,即使在Chrome控制台我可以看到一个区别:

 网​​:: ERR_INSECURE_RESPONSE
网:: ERR_CONNECTION_REFUSED
 

的responseText 始终是和状态code 始终为0两种情况。

我的问题是,我怎么能确定一个jQuery的AJAX调用失败,因为 ERR_CONNECTION_REFUSED ERR_INSECURE_RESPONSE 或由于?

在该证书被接受​​一切工作正常,但我想知道本地主机服务器是否被关闭,或者它的启动和运行,但该证书还没有被接受。

  $。阿贾克斯({
    键入:GET,
    网址:https://开头本地主机/客户/服务器/,
    数据类型:JSON,
    异步:真正的,
    成功:函数(响应){
        //做一点事
    },
    错误:函数(XHR,textStatus,errorThrown){
        的console.log(XHR,textStatus,errorThrown); //总是相同的拒绝和不安全的响应。
    }
});
 

即使手动执行我得到同样结果的请求:

  VAR请求=新XMLHtt prequest();
request.open(GET,https://开头本地主机/客户/服务器/,真正的);
request.onload =功能(){
    执行console.log(request.responseText);
};
request.onerror =功能(){
    执行console.log(request.responseText);
};
request.send();
 

解决方案

注意:我复制一个类似的环境。如果有人要运行一些测试,并尝试一些附带记思路,code是在回答底部,所以没必要自己去做。

关于这个问题:

在进行更多的研究,我结束了与此:

有没有办法从浏览器区分开来

根据W3C规范:

  

下面的步骤描述一下用户端必须做一个简单的跨域请求

     

应用提出申请步骤和观察,同时提出要求如下申请的规则。

     

如果手动重定向标志没有设置,响应具有301 HTTP状态code,302,303,307,或308   应用重定向步骤。

     

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

     

如果有网络错误   万一的DNS错误,TLS协商失败,或其他类型的网络错误的,则应用网络错误的步骤。不要求任何最终用户的交互。

     

注:不包括HTTP响应,表明某些类型的错误,如HTTP状态code 410

     

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

正如你可以看到,网络错误不包括HTTP响应,包括错误的,这就是为什么你会得到一直为0的状态code和的错误。

来源

证据和放大器;测试:

当我看到这个职位的第一次,我认为这一种方法可以做到通过HTTP来代替HTTPS就像@Schultzie指向他的回答次要请求,但记得那是不可能的,因为新版本的浏览器阻止混合内容(不能有HTTPS和HTTP在同一时间),这样的做法可以工作,但只有当你正在使用旧的浏览器如火狐它下面的版本23。

要测试它,使用上述环境的例子,如果你去的https://本地主机:8000 使用铬版43.0.2357.130

和过去的这个code。在控制台:

  VAR请求=新XMLHtt prequest();
request.open(GET,HTTP://本地主机:8001,真正的);
request.onload =功能(){
    执行console.log(request.responseText);
};
request.onerror =功能(){
    执行console.log(request.responseText);
};
request.send();
 

导致以下错误:

  

混合内容:在页面的https://本地主机:8000 / 加载通过HTTPS,但要求一个不安全的XMLHtt prequest端点'的http://本地主机:8001 / 。该请求被禁止的;内容必须通过HTTPS提供。

如果您尝试使用iframe,你应该得到完全相同的错误

 < IFRAME SRC =HTTP://本地主机:8001>< / IFRAME>
 

那么,舍弃HTTP通过HTTPS。

我也想接,但我艰难的话,它会在相同的结果结束,而是因为它被张贴作为一个答案,我已经试过了。但, 再次,混合内容是被禁止的:

粘贴在Chrome控制台

 新的WebSocket(WS://本地主机:8001,protocolOne);
 

结束在两个错误指向相同的问题:

  

1)混合内容:在页面的https://本地主机:8000 / '装载了HTTPS,但是尝试连接到不安全的WebSocket端点WS://本地主机:8001 /'。该请求被禁止的;这个端点必须提供在WSS。

     

2)未捕获抛出:DOMException:无法构造的WebSocket:一个不安全的WebSocket连接可能无法从加载通过HTTPS页面发起

然后我试着使用WSS粘贴此控制台连接

  VAR exampleSocket =新的WebSocket(WSS://本地主机:8001,protocolOne);
exampleSocket.onerror =功能(E){
    执行console.log(E);
}
 

然而,这改变了浏览器错误:

服务器关闭:

  

WebSocket连接到WSS://本地主机:8001 /'失败:在连接建立错误:网:: ERR_CONNECTION_REFUSED

服务器在:

  

WebSocket连接到WSS://本地主机:8001 /'失败:WebSocket的开放握手被取消

但同样,错误的onError的功能输出到控制台上没有任何提示,以区分其他的一个错误。

可能的解决方法:

使用代理作为@artur grzesiak回答可以工作,但仅在目标服务器具有公共访问,我韧这不是这里的情况,因此要实现在这种情况下,代理的唯一方式是使用一个应用程序,可使用本地主机,以测试它可以被克隆目标服务器,使从克隆服务器的请求的目标服务器,以及验证应答服务器侧的一个方法,但我们应该接受证书上的克隆服务器(我们的代理),并且导致相同的问题。

关于code复制环境

我创建了两个Node.js的HTTPS服务器,使用自签名的证书,

targetServer.js:

  VAR HTTPS =需​​要('HTTPS');
变种FS =要求(FS);

VAR的选择= {
    关键:fs.readFileSync(./ certs2 / key.pem),
    证书:fs.readFileSync(./ certs2 /关键cert.pem)
};

https.createServer(选项,函数(REQ,RES){
    res.setHeader(访问控制 - 允许 - 原产地','*');
    res.setHeader(访问控制 - 允许 - 方法,GET,PUT,POST,DELETE);
    res.setHeader(访问控制 - 允许 - 头,Content-Type的');
    res.writeHead(200);
    res.end(世界你好\ N);
})听(8001);
 

applicationServer.js:

  VAR HTTPS =需​​要('HTTPS');
变种FS =要求(FS);

VAR的选择= {
    关键:fs.readFileSync(./证书/ key.pem),
    证书:fs.readFileSync(./证书/密钥cert.pem)
};

https.createServer(选项,函数(REQ,RES){
    res.writeHead(200);
    res.end(世界你好\ N);
})听(8000);
 

要使它工作,你需要有Node.js的安装,需要产生分离证书构成每个服务器并将其存储在文件夹中的证书和certs2相应。在此之后,运行它像节点applicationServer.js 在一个终端节点targetServer.js

I've been doing a lot of research and could not find a way to handle this. I'm trying to perform a jQuery ajax call from an https server to a locahost https server running jetty with a custom self signed certificate. My problem is that I cannot determine whether the response is a connection refused or a insecure response (due to the lack of the certificate acceptance). Is there a way to determine the difference between both scenarios? The responseText, and statusCode are always the same in both cases, even though in the chrome console I can see a difference:

net::ERR_INSECURE_RESPONSE
net::ERR_CONNECTION_REFUSED

responseText is always "" and statusCode is always "0" for both cases.

My question is, how can I determine if a jQuery ajax call failed due to ERR_INSECURE_RESPONSE or due to ERR_CONNECTION_REFUSED?

Once the certificate is accepted everything works fine, but I want to know whether the localhost server is shut down, or its up and running but the certificate has not yet been accepted.

$.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.
    }
});

Even performing manually the request I get the same result:

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();

解决方案

Note: I've replicated a similar environment. If someone want to run some test and to try some of the ideas that come in mind, the code is at the bottom of the answer, so no need to do it by yourself.

About the question:

After a bit more of research I've ended up with this:

There is no way to differentiate it from the browser

According to W3C Spec:

The steps below describe what user agents must do for a simple cross-origin request:

Apply the make a request steps and observe the request rules below while making the request.

If the manual redirect flag is unset and the response has an HTTP status code of 301, 302, 303, 307, or 308 Apply the redirect steps.

If the end user cancels the request Apply the abort steps.

If there is a network error In case of DNS errors, TLS negotiation failure, or other type of network errors, apply the network error steps. Do not request any kind of end user interaction.

Note: This does not include HTTP responses that indicate some type of error, such as HTTP status code 410.

Otherwise Perform a resource sharing check. If it returns fail, apply the network error steps. Otherwise, if it returns pass, terminate this algorithm and set the cross-origin request status to success. Do not actually terminate the request.

As you can read, network errors does not include HTTP response that include errors, that is why you will get always 0 as status code, and "" as error.

Source

Evidences & Tests:

When I saw this post for the first time I think that an approach could be to do a secondary request over HTTP instead of HTTPS like @Schultzie pointed in his answer but remembered that is not possible because newer versions of browsers block the mixed content (cant have HTTPS and HTTP at the same time), so that approach could work, but only if you are using older browser like Mozilla Firefox below it version 23.

to test it, Using the mentioned environment example, if you go to https://localhost:8000 using chrome Version 43.0.2357.130

And past this code in console:

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();

result in the following error:

Mixed Content: The page at 'https://localhost:8000/' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://localhost:8001/'. This request has been blocked; the content must be served over HTTPS.

If you try to use an Iframe, you should get the same exact error

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

So, discarded HTTP over HTTPS.

I've also think in Sockets but I tough then that it would end in the same result, but because it was posted as an answer, I've tried it. But, once again, mixed content is forbidden:

Pasting in the chrome console

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

end up in two errors pointing to the same problem:

1) Mixed Content: The page at 'https://localhost:8000/' was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint 'ws://localhost:8001/'. This request has been blocked; this endpoint must be available over WSS.

2) Uncaught DOMException: Failed to construct 'WebSocket': An insecure WebSocket connection may not be initiated from a page loaded over HTTPS.

Then I've tried connecting using wss pasting this in the console

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

However this changed the browser errors to:

Server turned off:

WebSocket connection to 'wss://localhost:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED

Server On:

WebSocket connection to 'wss://localhost:8001/' failed: WebSocket opening handshake was canceled

But again, the error that the "onerror function" output to the console have not any tip to differentiate one error of the other.

Possible workaround:

Using a proxy as @artur grzesiak answered could work but only if the "target" server has public access, I tough that is not the case here, so the only way to implement a proxy in this case is using an application that have access to "localhost", one way to test it could be cloning the "target Server" and making a request from the "cloned" server to the "target server", and validating the response server side, but we should accept the certificates on "cloned" server (our proxy) and that lead to the same problems.

About the code to replicate the environment:

I've created two Node.js HTTPS servers, that use self signed certificates,

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);

To make it work you need to have Node.js Installed, Need to generate separated certificates form each server and store it in the folders certs and certs2 accordingly. After that, run it like node applicationServer.js and node targetServer.js in a terminal.

这篇关于确定是否Ajax调用由于不安全的响应或拒绝连接失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆