Визначте, чи не вдалося здійснити виклик Ajax через незахищену відповідь або відмовлено у зв'язку


115

Я робив багато досліджень і не міг знайти способу впоратися з цим. Я намагаюся здійснити виклик jQuery ajax з https-сервера на локальний сервер https-сервера, на якому працює jetty, зі спеціальним сертифікатом самопідписання. Моя проблема полягає в тому, що я не можу визначити, чи було у відповіді відмовлено у зв’язку чи невпевнено відповідати (через відсутність прийняття сертифіката). Чи є спосіб визначити різницю між обома сценаріями? responseText, І statusCodeзавжди однакові в обох випадках, навіть якщо в хромованою консолі я можу побачити різницю:

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

введіть тут опис зображення

Навіть виконуючи запит вручну, я отримую той же результат:

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. Будь ласка, уважно прочитайте моє запитання.
таксакала

1
Чи надають інші два аргументи зворотного виклику помилок додаткове розуміння? function (xhr, status, msg) {...Сумніваюся, що вони будуть, але варто спробувати.
Кевін Б

2
Ні. Від сервера до іншого сервера. Сервер пристані (працює в locahost) правильно встановив заголовки CORS, тому що як тільки я приймаю сертифікат, все працює як очікувалося. Я хочу визначити, чи потрібно прийняти сертифікат чи пристань знищена.
таксакала

5
Цілком можливо, що брак інформації у браузері цілком навмисний. Просто «помилка» надасть деяку кількість інформації запропонованому хакеру, наприклад. "Ця система має щось прослуховування на порту тощо."
Katana314

1
це не те, що я хочу, це щось, що мені потрібно за дизайном та характером того, що я розвиваю. сторона сервера - це не варіант.
таксікала

Відповіді:


67

Немає можливості відрізнити це від найновіших веб-браузерів.

Специфікація W3C:

Наведені нижче кроки описують, що агенти користувачів повинні виконувати для простого запиту між походженням :

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

Якщо прапор перенаправлення вручну не встановлено, а відповідь має код статусу HTTP 301, 302, 303, 307 або 308, застосуйте кроки перенаправлення.

Якщо кінцевий користувач скасує запит, застосуйте етапи переривання.

Якщо є мережна помилка У разі помилок DNS, помилки узгодження TLS або іншого типу мережевих помилок, застосуйте кроки мережевої помилки . Не вимагайте жодної взаємодії з кінцевим користувачем.

Примітка. Це не включає відповіді HTTP, які вказують на певний тип помилок, наприклад код HTTP статусу 410.

Інакше проведіть перевірку обміну ресурсами. Якщо він не повертається, застосуйте кроки помилки мережі. В іншому випадку, якщо він повертає пропуск, скасуйте цей алгоритм і встановіть статус запиту перехресного походження на успіх. Насправді не припиняйте запит.

Як ви можете прочитати, мережеві помилки не включають відповідь HTTP, яка включає помилки, тому ви завжди отримаєте 0 як код статусу, а "" - як помилка.

Джерело


Примітка . Наступні приклади зроблені за допомогою версії Google Chrome 43.0.2357.130 та проти середовища, яке я створив для імітації ОП. Код до його налаштування знаходиться внизу відповіді.


Хоча я вважаю, що підхід для вирішення цього питання може бути вторинним запитом через HTTP замість HTTPS як ця відповідь, але я пам’ятав, що це неможливо через те, що новіші версії браузерів блокують змішаний контент.

Це означає, що веб-браузер не дозволить запитувати через HTTP, якщо ви використовуєте HTTPS і навпаки.

Це було подібним з декількох років тому, але старіші версії веб-браузера, такі як Mozilla Firefox, нижче його версії 23 дозволяють.

Докази про це:

Здійснення HTTP-запиту з консолі веб-браузера HTTPS

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 до не захищеної кінцевої точки сокета закінчиться помилками змішаного вмісту.

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 скасовано

Але знову ж таки помилка в тому, що "конверторна функція" виводиться на консоль, не має жодної підказки для розмежування однієї помилки від іншої.


Використання проксі-сервера, як підказує ця відповідь, може спрацювати, але лише у тому випадку, якщо "цільовий" сервер має доступ до публіки.

Тут цього не було, тому спроба впровадити проксі в цьому сценарії призведе нас до тієї ж проблеми.

Код для створення сервера HTTPS Node.js :

Я створив два сервери 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. Потрібно генерувати окремі сертифікати для кожного сервера та зберігати їх у серцях папок та certs2 відповідно.

Для його запуску просто виконуйте node applicationServer.jsі node targetServer.jsв терміналі (приклад ubuntu).


6
Це найповніша відповідь поки що. Це показує, що ви насправді виконали деякі дослідницькі роботи та тестування. Я бачу, що ви намагалися всіляко здійснити це, і всі сценарії справді добре пояснені. Ви також надали приклад коду для швидкого налаштування тестового середовища! Дійсно хороша робота! Дякуємо за розуміння!
таксікала

Відмінна відповідь! Однак я хотів би зазначити, що сертифікати, які самостійно підписують, все одно створюватимуть помилки у браузері, якщо вони з окремих серверів. @ecarrizo
FabricioG

Якщо ми говоримо про мережеву помилку, напевно, ви зможете побачити помилки в консолі вручну. але ви не маєте доступу до нього з коду в захищеному середовищі.
ecarrizo

32

На сьогоднішній день: Не існує можливості розмежувати цю подію між броузерами. Оскільки веб-переглядачі не надають розробникам події для доступу.(Липень 2015 р.)

Ця відповідь просто спрямована на те, щоб запропонувати ідеї для потенційного, нескінченного та неповного рішення Альбіет.


Відмова від відповідальності: ця відповідь є неповною, оскільки не повністю вирішує проблеми ОП (через політику перехресного походження). Однак сама ідея має певну заслугу, яку далі розширює: @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, браузер скасує його відразу, оскільки ви не можете робити запити ajax з сервера HTTPS на сервер HTTP
taxicala

11

@ Відповідь Шульцзі досить близька, але однозначно http- загалом - не вийдеhttps в середовищі браузера.

Однак ви можете зробити проміжний сервер (проксі), щоб зробити запит від вашого імені. Проксі-сервер повинен дозволяти або пересилати httpзапит від httpsпоходження, або завантажувати вміст із власним підписом .

Наявність власного сервера з належним сертифікатом - це, мабуть, надлишок у вашому випадку - так як ви можете використовувати це налаштування замість машини із сертифікатом, який підписав власноруч - але там є безліч анонімних відкритих проксі-сервісів .

Тож мені підходять два підходи:

  1. ajax-запит - у такому випадку проксі повинен використовувати відповідні налаштування CORS
  2. використання iframe - ви завантажуєте свій скрипт (можливо, загорнутий у html) всередині iframe через проксі. Після завантаження сценарію він надсилає повідомлення своєму .parentWindow. Якщо у вашому вікні надійшло повідомлення, ви можете бути впевнені, що сервер працює (а точніше, запускав частку секунди раніше).

Якщо вас цікавить лише ваше місцеве середовище, ви можете спробувати запустити хром із --disable-web-securityпрапором.


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


1

Перевіряти jQuery.ajaxError () Посилання на: jQuery AJAX Помилка обробкою помилок (коди HTTP-статусу) Він виявляє глобальні помилки Ajax, з якими ви можете вирішувати будь-яку кількість способів через HTTP або HTTPS:

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

Це те саме, що робити аякс так, як я це роблю, але централізувати відповіді на помилки в одному місці.
таксікала

1
Справа в тому, що сертифікат не завантажується під час виклику ajax, як здається, це проблема. будь-якими способами з пошуку відповідей :)
Varshaan

2
Ні, проблема полягає в тому, що я хочу визначити різницю між тим, що потрібно прийняти сертифікат, і знати, чи закрито сервер.
таксакала

1
І коли відмовлено в з'єднанні, і в сертифікаті не довіряють, я отримую той самий jqXHR.status = 0 (і jqXHR.readyState = 0), а не qXHR.status == 102 або 501, як описано в цій відповіді.
anre

1

На жаль, сучасний API XHR браузера не дає явної вказівки на те, коли браузер відмовляється підключитися через "незахищену відповідь", а також коли він не довіряє 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.de


1

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.