Определить, произошел ли сбой вызова ajax из-за небезопасного ответа или соединения

я провел много исследований и не смог найти способ справиться с этим. Я пытаюсь выполнить вызов jQuery ajax с сервера https на сервер https locahost, на котором работает причал с пользовательским самозаверяющим сертификатом. Моя проблема заключается в том, что я не могу определить, является ли ответ отказом от соединения или небезопасным ответом (Из-за отсутствия принятия сертификата). Есть ли способ определить разницу между обоими сценариями? The responseText и statusCode всегда одинаковы в обоих случаях, хотя в консоли chrome я вижу разницу:

net::ERR_INSECURE_RESPONSE
net::ERR_CONNECTION_REFUSED

responseText - это всегда "" и statusCode всегда " 0 " для обоих случаев.

мой вопрос в том, как я могу определить, не удалось ли вызвать jQuery ajax из-за ERR_INSECURE_RESPONSE или из-за ERR_CONNECTION_REFUSED?

как только сертификат принят, все работает нормально, но я хочу знать, выключен ли сервер localhost или он запущен и работает, но сертификат еще не был общепринятый.

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

6 ответов


нет способа отличить его от новейших веб-браузеров.

спецификация W3C:

шаги ниже описывают, что должны делать агенты пользователей для простого запроса перекрестного происхождения:

применить сделать запрос шаги и соблюдать правила запроса ниже при выполнении запроса.

если флаг перенаправления вручную не установлен и ответ имеет код состояния HTTP 301, 302, 303, 307 или 308 Примените шаги перенаправления.

если пользователь отменяет запрос Примените шаги отмены.

если есть сетевая ошибка В случае ошибок DNS, сбоя согласования TLS или других сетевых ошибок примените действия сети ошибка. Не запрашивайте никакого взаимодействия с конечным пользователем.

Примечание: это не включает HTTP-ответы, которые указывают на некоторые тип ошибки, например код состояния HTTP 410.

иначе Выполните проверку общего доступа к ресурсам. Если он возвращает fail, примените шаги сетевой ошибки. В противном случае, если он возвращает pass, завершите этот алгоритм и установите состояние запроса cross-origin в success. Фактически не прекращайте запрос.

как вы можете прочитать, сетевые ошибки не включают HTTP-ответ, который включает ошибки, поэтому вы всегда получите 0 как код состояния и "" как ошибка.

источник


Примечание: следующие примеры были сделаны с использованием Google Chrome версии 43.0.2357.130 и против среды, которую я создал для эмуляции OP one. Код для настройки находится в нижней части ответа.


Я думал, что подход к работе вокруг этого будет делать вторичный запрос по HTTP вместо HTTPS как ответ но я вспомнил, что это невозможно из-за того, что более новые версии браузеров блокируют смешанный контент.

это означает, что веб-браузер не будет разрешать запрос по HTTP, если вы используете HTTPS и наоборот.

это было так несколько лет назад, но более старые версии веб-браузера, такие как Mozilla Firefox ниже версии 23 позволяют это.

доказательства об этом:

создание HTTP-запроса от HTTPS usign Web Broser консоль

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>

использование сокета также было опубликовано в качестве ответа, я был уверен, что результат будет таким же / похожим, но я попробовал.

попытка открыть соединение сокета из веб-Broswer с помощью HTTPS к небезопасной конечной точке сокета закончится ошибками смешанного содержимого.

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

1) смешанный контент: страница в'https://localhost:8000/' был загружен через HTTPS, но попытался для подключения к небезопасной конечной точке WebSocket 'ws:/ / localhost: 8001/'. Этот запрос заблокирован; эта конечная точка должна быть доступна через WSS.

2) Uncaught DOMException: не удалось создать "WebSocket": небезопасное соединение WebSocket не может быть инициировано со страницы, загруженной через HTTPS.

затем я попытался подключиться к конечной точке 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 " выводит на консоль не имейте никакого подсказки, чтобы отличить одну ошибку от другой.


через прокси, как этот ответ предлагаю может работать, но только если "целевой" сервер имеет открытый доступ.

здесь это не так, поэтому попытка реализовать прокси в этом сценарии приведет нас к той же проблеме.

код для создания узла.JS HTTPS server:

Я создал два сервера HTTPS Nodejs, которые используют самозаверяющие сертификаты:

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.js и node targetServer.js в терминале (пример ubuntu).


сейчас: нет никакого способа отличить это событие от browers. Поскольку браузеры не предоставляют разработчикам доступ к событию. (июль 2015 года)

этот ответ просто стремится предоставить идеи для потенциального, albiet hacky и неполного решения.


предупреждение: этот ответ является неполным, так как не полностью решить проблемы 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();

конечно, для этого требуется дополнительный запрос для проверки подключения сервера, но он должен выполнить работу путем исключения. Тем не менее, я все еще чувствую себя хакером, и я не большой поклонник двойного запроса.


ответ@Schultzie довольно близок, но ясно http - В общем - не получится из https в среде браузера.

что вы можете сделать, это использовать промежуточный сервер (Прокси), чтобы сделать запрос от вашего имени. Прокси-сервер должен разрешить пересылку http запрос от https origin или загрузить содержимое из самоподписанный происхождение.

наличие собственного сервера с соответствующим сертификатом, вероятно, является излишним в вашем случае -- как вы могли бы использовать этот параметр вместо машины с самозаверяющий сертификат, но есть много анонимные открытые прокси-сервисов там.

Итак, два подхода, которые приходят мне на ум:

  1. ajax запрос -- в таком случае прокси должен использовать соответствующие настройки CORS
  2. использование iframe -- вы загружаете свой скрипт (возможно, завернутый в html) внутри iframe через прокси. Однажды загруженный скрипт отправляет сообщение своему .parentWindow. Если ваше окно получило сообщение, вы можете быть уверены, что сервер работает (или, точнее, был запущен за долю секунды до этого).

если вас интересует только локальная среда, Вы можете попробовать запустить chrome с помощью --disable-web-security флаг.


другое предложение: вы пытались загрузить изображение программно, чтобы узнать, есть ли там больше информации?


проверить С помощью jQuery.ajaxError() Взято из ссылки:обработка ошибок jQuery AJAX (коды состояния HTTP) Он ловит глобальные ошибки Ajax, которые вы можете обрабатывать любым количеством способов через HTTP или HTTPS:

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

к сожалению, современный браузер XHR API не предоставляет явного указания на то, когда браузер отказывается подключаться из-за "небезопасного ответа", а также когда он не доверяет сертификату HTTP/SSL веб-сайта.

но есть способы обойти эту проблему.

одно решение, которое я придумал, чтобы определить, когда браузер не доверяет сертификату HTTP / SSL, - это сначала определить, произошла ли ошибка XHR (используя jQuery error() обратный звонок для экземпляр), затем проверьте, является ли вызов XHR URL-адресом " https://", а затем проверьте, является ли XHR readyState равно 0, что означает, что соединение XHR даже не было открыто (что происходит, когда браузеру не нравится сертификат).

вот код, где я это делаю: https://github.com/maratbn/RainbowPayPress/blob/e9e9472a36ced747a0f9e5ca9fa7d96959aeaf8a/rainbowpaypress/js/le_requirejs/public/model_info__transaction_details.js#L88


Я не думаю, что в настоящее время есть способ обнаружить эти сообщения об ошибках, но хак, который вы можете сделать, это использовать сервер, такой как nginx перед вашим сервером приложений, так что если сервер приложений не работает, вы получите плохую ошибку шлюза от nginx с 502 код состояния, который вы можете обнаружить в JS. В противном случае, если сертификат недействителен, вы все равно получите ту же общую ошибку с statusCode = 0.