it-swarm.dev

안전하지 않은 응답 또는 연결 거부로 인해 Ajax 호출이 실패했는지 판별

나는 많은 연구를 해왔고 이것을 처리하는 방법을 찾지 못했습니다. https 서버에서 사용자 정의 자체 서명 인증서로 부두를 실행하는 locahost https 서버로 jQuery ajax 호출을 수행하려고합니다. 내 문제는 (인증서 승인이 없기 때문에) 응답이 연결 거부인지 또는 안전하지 않은 응답인지 여부를 확인할 수 없다는 것입니다. 두 시나리오의 차이점을 결정하는 방법이 있습니까? chrome 콘솔에서 차이점을 볼 수 있지만 responseTextstatusCode은 두 경우 모두 항상 동일합니다.

net::ERR_INSECURE_RESPONSE
net::ERR_CONNECTION_REFUSED

두 경우 모두 responseText은 항상 ""이고 statusCode은 항상 "0"입니다.

제 질문은 ERR_INSECURE_RESPONSE 또는 ERR_CONNECTION_REFUSED로 인해 jQuery ajax 호출이 실패했는지 어떻게 확인할 수 있습니까?

인증서가 수락되면 모든 것이 잘 작동하지만 로컬 호스트 서버가 종료되었는지 또는 실행 중이지만 인증서가 아직 수락되지 않았는지 알고 싶습니다.

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

enter image description here

수동으로 요청을 수행해도 동일한 결과가 나타납니다.

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

최신 웹 브라우저와 구별 할 수있는 방법이 없습니다.

W3C 사양 :

아래 단계는 간단한 교차 출처 요청에 대해 사용자 에이전트가 수행해야하는 작업을 설명합니다 :

요청 단계를 적용하고 요청하는 동안 아래 요청 규칙을 준수하십시오.

수동 리디렉션 플래그가 설정되어 있지 않고 응답의 HTTP 상태 코드가 301, 302, 303, 307 또는 308 인 경우 리디렉션 단계를 적용하십시오.

최종 사용자가 요청을 취소하는 경우 중단 단계를 적용하십시오.

네트워크 오류가있는 경우 DNS 오류, TLS 협상 실패 또는 기타 유형의 네트워크 오류의 경우 네트워크 오류 단계 를 적용하십시오. 어떤 종류의 최종 사용자 상호 작용도 요청하지 마십시오.

참고 : HTTP 상태 코드 410과 같은 일부 유형의 오류를 나타내는 HTTP 응답은 포함되지 않습니다.

Otherwise 리소스 공유 확인을 수행하십시오. 실패가 리턴되면 네트워크 오류 단계를 적용하십시오. 그렇지 않으면 전달이 리턴되면이 알고리즘을 종료하고 교차 오리진 요청 상태를 성공으로 설정하십시오. 실제로 요청을 종료하지 마십시오.

읽을 수 있듯이 네트워크 오류에는 오류가 포함 된 HTTP 응답이 포함되어 있지 않으므로 항상 상태 코드로 0을, 오류로 ""를 얻는 것입니다.

소스


Note : 다음 예제는 Google Chrome 버전 43.0.2357.130을 사용하여 OP를 에뮬레이트하기 위해 만든 환경에 대해 작성되었습니다. 설정 된 코드는 답변의 맨 아래에 있습니다.


나는 이것을 해결하는 접근법이 This answer 와 같이 HTTPS 대신 HTTP를 통해 보조 요청을하는 것이지만 최신 버전의 브라우저가 혼합 된 콘텐츠를 차단하기 때문에 불가능하다는 것을 기억했습니다.

이는 HTTPS를 사용하거나 그 반대의 경우 웹 브라우저가 HTTP를 통한 요청을 허용하지 않음을 의미합니다.

이것은 몇 년 전부터 이와 같았지만 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>

소켓 연결을 사용하는 것도 답으로 게시 됨 , 결과가 같거나 비슷하다는 것을 확신했지만 시도해 보았습니다.

HTTPS를 사용하여 Web Broswer에서 비보안 소켓 엔드 포인트로의 소켓 연결을 열려고하면 혼합 컨텐츠 오류가 발생합니다.

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

1) 혼합 컨텐츠 : ' https : // localhost : 8000 / '의 페이지가 HTTPS를 통해로드되었지만 안전하지 않은 WebSocket 엔드 포인트 'ws : // localhost : 8001 /'에 연결을 시도했습니다. 이 요청은 차단되었습니다. 이 엔드 포인트는 WSS를 통해 사용 가능해야합니다.

2) Uncaught DOMException : 'WebSocket'을 구성하지 못했습니다 : HTTPS를 통해로드 된 페이지에서 안전하지 않은 WebSocket 연결을 시작할 수 없습니다.

그런 다음 wss 엔드 포인트에 연결하려고 시도했지만 네트워크 연결 오류에 대한 정보를 읽을 수 있는지 확인하십시오.

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

서버를 끈 상태에서 위의 스 니펫을 실행하면 다음과 같은 결과가 발생합니다.

'wss : // localhost : 8001 /'에 대한 WebSocket 연결 실패 : 연결 설정 오류 : net :: ERR_CONNECTION_REFUSED

서버가 켜진 상태에서 위의 스 니펫 실행

'wss : // localhost : 8001 /'에 대한 WebSocket 연결 실패 : WebSocket 열기 핸드 셰이크가 취소되었습니다

그러나 "onerror function"이 콘솔에 출력한다는 오류는 다른 오류를 구별 할 수있는 팁이 없습니다.


프록시를 이 답변 제안 으로 사용하면 "대상"서버에 공개 액세스 권한이있는 경우에만 작동 할 수 있습니다.

여기에는 해당되지 않았으므로이 시나리오에서 프록시를 구현하려고하면 동일한 문제가 발생합니다.

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을 실행하십시오 (예시).

66
ecarrizo

현재 : 이 이벤트를 브라우저간에 구분할 방법이 없습니다. 브라우저는 개발자가 액세스 할 수있는 이벤트를 제공하지 않기 때문에 (2015 년 7 월)

이 답변은 단지 잠재적이고 애매한 해 키고 불완전한 솔루션에 대한 아이디어를 제공하려고합니다.


면책 조항 : 이 답변은 완전히 해결되지 않기 때문에 불완전합니다. 원산지 정책). 그러나 아이디어 자체에는 프록시와 아약스를 사용하여 @ artur grzesiak here에 의해 확장되는 장점이 있습니다. .


약간의 연구를 한 후에 적어도 자바 스크립트가 두 가지의 차이에 대한 응답을 제공하는 한 연결 거부와 안전하지 않은 응답의 차이에 대한 오류 검사 형식이없는 것 같습니다.

내 연구의 일반적인 합의는 SSL 인증서가 브라우저에서 처리되므로 사용자가 자체 서명 인증서를 수락 할 때까지 브라우저는 상태 코드에 대한 요청을 포함하여 모든 요청을 잠급니다. 브라우저는 (부호화 된 경우) 안전하지 않은 응답을 위해 자체 상태 코드를 다시 보낼 수는 있지만 실제로 도움이되지는 않으며 브라우저 호환성 (다른 표준을 가진 크롬/파이어 폭스/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();

서버 연결을 확인하기 위해 추가 요청이 필요하지만 제거 프로세스로 작업을 완료해야합니다. 그래도 여전히 해 키고, 나는 이중 요청의 열렬한 팬이 아닙니다.

31
acupajoe

@Schultzie의 대답은 매우 가깝지만 브라우저 환경의 http에서는 일반적으로 https-작동하지 않습니다.

당신이 할 수있는 일은 당신을 대신하여 요청을하기 위해 중간 서버 (프록시)를 사용하는 것입니다. 프록시는 http 오리진에서 https 요청을 전달하거나 자체 서명 오리진 에서 콘텐츠를로드 할 수 있어야합니다.

자체 서명 된 인증서가있는 시스템 대신이 설정을 사용할 수 있기 때문에 적절한 인증서를 가진 자체 서버를 보유하는 것은 아마도 과도합니다.하지만 익명으로 많은 프록시 서비스 가 있습니다.

내 마음에 오는 두 가지 접근 방식은 다음과 같습니다.

  1. ajax request-이 경우 프록시는 적절한 CORS 설정을 사용해야합니다
  2. iframe 사용-프록시를 통해 iframe 내부에 스크립트 (html로 래핑 됨)를로드합니다. 스크립트가로드되면 .parentWindow에 메시지를 보냅니다. 창에 메시지가 표시되면 서버가 실행 중인지 또는보다 정확하게 1 초 전에 실행 중인지 확인할 수 있습니다.

로컬 환경에만 관심이있는 경우 --disable-web-security 플래그와 함께 chrome을 (를) 실행 해보십시오.


또 다른 제안 : 더 많은 정보가 있는지 확인하기 위해 프로그래밍 방식으로 이미지를로드하려고 했습니까?

11
artur grzesiak

불행히도 현재 브라우저 XHR API는 "안전하지 않은 응답"으로 인해 브라우저가 연결을 거부 할 때와 웹 사이트의 HTTP/SSL 인증서를 신뢰하지 않는 경우에 대한 명시적인 표시를 제공하지 않습니다.

그러나이 문제를 해결할 방법이 있습니다.

브라우저가 HTTP/SSL 인증서를 신뢰하지 않을 때를 결정하기 위해 내놓은 한 가지 해결책은 먼저 XHR 오류가 발생했는지 여부를 먼저 감지하는 것입니다 (예 : jQuery error() 콜백 사용) .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
maratbn

나는 현재 이러한 오류 메시지를 감지하는 방법이 없다고 생각하지만, 할 수있는 해킹은 응용 프로그램 서버 앞에서 nginx와 같은 서버를 사용하는 것입니다. 따라서 응용 프로그램 서버가 다운되면 나빠질 것입니다 JS에서 감지 할 수있는 502 상태 코드가있는 nginx의 게이트웨이 오류. 그렇지 않으면 인증서가 유효하지 않은 경우에도 statusCode = 0와 동일한 일반 오류가 발생합니다.

1
Gaafar

jQuery.ajaxError () 참조 : jQuery AJAX 오류 처리 (HTTP 상태 코드) 처리 할 수있는 전역 Ajax 오류를 포착합니다. HTTP 또는 HTTPS를 통한 다양한 방법 :

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