Déterminer si l'appel ajax a échoué en raison d'une réponse non sécurisée ou d'un refus de connexion

j'ai fait beaucoup de recherche, et ne pouvait pas trouver un moyen de gérer cela. J'essaie d'effectuer un appel https://over.wiki/ajax/" class="blnk">ajax jQuery depuis un serveur https vers un serveur https locahost qui exécute jetty avec un certificat auto-signé personnalisé. Mon problème est que je ne peux pas déterminer si la réponse est une connexion refusée ou une réponse non sécurisée (en raison de l'absence de l'acceptation du certificat). Est-il un moyen de déterminer la différence entre les deux scénarios? Les responseText , et statusCode sont toujours la même chose dans les deux cas, même si dans la console chrome je peux voir une différence:

net::ERR_INSECURE_RESPONSE
net::ERR_CONNECTION_REFUSED

responseText est toujours "" et statusCode est toujours à "0" pour les deux cas.

ma question Est, Comment puis-je déterminer si un appel ajax de jQuery a échoué en raison de ERR_INSECURE_RESPONSE ou en raison de ERR_CONNECTION_REFUSED ?

une fois que le certificat est accepté tout fonctionne bien, mais je veux savoir si le serveur localhost est arrêté, ou son et tourne, mais le certificat n'a pas encore été acceptée.

$.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

même en exécutant manuellement la requête, j'obtiens le même résultat:

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();
112
demandé sur taxicala 2015-06-25 22:07:18

6 réponses

il n'y a aucun moyen de le différencier des plus récents navigateurs web.

spécification W3C:

les étapes ci-dessous décrivent ce que les agents utilisateurs doivent faire pour une simple demande d'origine croisée :

appliquer les étapes faire une demande et respecter les règles de la demande ci-dessous lors de la présentation de la demande.

si le manuel le drapeau de redirection est désactivé et la réponse a un code D'état HTTP de 301, 302, 303, 307, ou 308 Appliquer les étapes de redirection.

si l'utilisateur final annule la demande Appliquer les étapes d'abandon.

S'il y a une erreur de réseau En cas d'erreurs DNS, d'échec de négociation TLS ou d'autres types d'erreurs réseau, appliquer les étapes d'erreur réseau . Ne pas demandez n'importe quel type d'interaction avec l'utilisateur final.

Note: Ceci n'inclut pas les réponses HTTP qui indiquent un certain type d'erreur, comme le code de statut HTTP 410.

autrement Effectuer une vérification du partage des ressources. Si elle retourne fail, appliquer les étapes d'erreur réseau. Sinon, s'il renvoie pass, résolvez cet algorithme et définissez l'état de la requête d'origine croisée à succès. Ne sont pas réellement mettre fin à la demande.

comme vous pouvez le lire, les erreurs réseau n'incluent pas les réponses HTTP qui incluent les erreurs, c'est pourquoi vous obtiendrez toujours 0 comme code d'état, et "" comme erreur.

Source


Note : les exemples suivants ont été produits en utilisant Google Chrome version 43.0.2357.130 et contre une l'environnement que j'ai créé pour émuler OP. Code pour le jeu il est en bas de la réponse.


j'ai pensé qu'une approche pour contourner cela serait de faire une requête secondaire sur HTTP au lieu de HTTPS comme cette réponse mais je me suis souvenu que ce n'est pas possible en raison du fait que les nouvelles versions de navigateurs bloquent le contenu mixte.

cela signifie que le navigateur web ne permettra pas une demande HTTP Si vous utilisez HTTPS et vice versa.

cela a été comme cela depuis quelques années, mais les anciennes versions de navigateur Web comme Mozilla Firefox en dessous de lui les versions 23 le permettent.

éléments de Preuve à ce sujet:

Faire une requête HTTP, HTTPS avec du navigateur Web de la 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();

entraînera l'erreur suivante:

mixte Contenu: la page à ""https://localhost:8000 / 'a été chargé sur HTTPS, mais a demandé un endpoint XMLHttpRequest non sécurisé ' http://localhost:8001 / ". Cette requête a été bloquée; le contenu doit être servi via HTTPS.

la même erreur apparaîtra dans la console du navigateur si vous essayez de le faire d'une autre manière en ajoutant une Iframe.

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

à l'Aide de La connexion Socket a aussi été posté comme réponse , j'étais assez sûr que le résultat sera le même / similaire mais j'ai donné un essai.

essayer d'ouvrir une connexion socket à partir du Broswer Web en utilisant HTTPS vers un point final de socket non sécurisé se terminera par des erreurs de contenu mixte.

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

1) contenu mixte: la page à https://localhost:8000 / " a été chargé sur HTTPS, mais tenté de se connecter au terminal WebSocket non sécurisé " ws://localhost:8001/". Cette demande a été bloquée; ce paramètre doit être disponible sur WSS.

2) Uncaught DOMException: N'a pas réussi à construire 'WebSocket': une connexion WebSocket non sécurisée ne peut pas être initialisée à partir d'une page chargée sur HTTPS.

alors j'ai essayé de me connecter à un terminal wss aussi voir si je pouvais lire quelques informations sur les erreurs de connexion réseau:

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

L'exécution de snippet ci-dessus avec les résultats du serveur désactivé:

connexion WebSocket 'wss://localhost:8001/" a échoué: Erreur dans l'établissement de la connexion: net::ERR_CONNECTION_REFUSED

exécuter snippet ci-dessus avec le serveur activé

connexion WebSocket 'wss://localhost:8001/" a échoué: WebSocket d'ouverture de la poignée de main a été annulée

mais encore une fois, l'erreur que la" fonction onerror " sortie à la console n'ont pas d'astuce pour différencier une erreur de l'autre.


en utilisant un proxy comme cette réponse suggère que pourrait fonctionner, mais seulement si le serveur "cible" a un accès public.

ce n'était pas le cas ici, donc essayer d'implémenter un proxy dans ce scénario nous mènera au même problème.

Code pour créer un noeud.js HTTPS server :

j'ai créé deux serveurs Nodejs HTTPS, qui utilisent des certificats auto-signés:

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

pour que cela fonctionne, vous devez avoir Nodejs installé, besoin de générer des certificats séparés pour chaque serveur et de le stocker dans les dossiers certs et certs2 en conséquence.

pour L'Exécuter il suffit d'exécuter node applicationServer.js et node targetServer.js dans un terminal (exemple ubuntu).

59
répondu ecarrizo 2017-05-23 10:31:09

à partir de Maintenant: il n'y a aucun moyen de différencier cet événement entre les browers. Comme les navigateurs ne fournissent pas un événement pour les développeurs d'accès. (juillet 2015)

Cette réponse ne fait que donner des idées pour un potentiel, albiet hacky et incomplète, la solution.


responsabilité: cette réponse est incomplète car elle ne complètement résoudre OP's problèmes (en raison des politiques sur l'origine croisée). Cependant l'idée elle-même a un certain mérite qui est développé plus loin par: @artur grzesiak ici , en utilisant un proxy et ajax.


Après pas mal de recherches moi-même, il ne semble y avoir aucune forme de vérification d'erreur pour la différence entre une connexion refusée et une réponse non sécurisée, au moins en ce qui concerne javascript fournissant une réponse pour le la différence entre les deux.

le consensus général de mes recherches étant que les certificats SSL sont traités par le navigateur, donc jusqu'à ce qu'un certificat auto-signé soit accepté par l'utilisateur, le navigateur bloque toutes les demandes, y compris celles pour un code d'état. Le navigateur pourrait (si codé) renvoyer son propre code d'État pour une réponse non sécurisée, mais cela n'aide pas vraiment quoi que ce soit, et même alors, vous auriez des problèmes avec la compatibilité du navigateur (chrome/firefox/IE ayant des normes différentes... encore une fois)

étant donné que votre question initiale était de vérifier l'état de votre serveur entre être en fonction et avoir un certificat inacceptable, ne pourriez-vous pas faire une requête HTTP standard comme cela?

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

accordé cela nécessite une requête supplémentaire pour vérifier la connectivité du serveur, mais il devrait obtenir le travail fait par un processus d'élimination. J'ai toujours l'impression d'être hacky, et je ne suis pas un grand fan de la double demande.

29
répondu acupajoe 2017-05-23 12:34:11

la réponse de @Schultzie est assez proche, mais clairement http - en général-ne fonctionnera pas à partir de https dans l'environnement du navigateur.

Ce que vous pouvez faire est d'utiliser un serveur intermédiaire (proxy) pour en faire la demande en votre nom. Le mandataire doit permettre soit de transmettre la demande http de https origin, soit de charger le contenu de self-signed origins .

avoir son propre serveur avec un bon certificat est probablement une exagération dans votre cas -- car vous pourriez utiliser ce paramètre à la place de la machine avec un certificat auto-signé -- mais il y a beaucoup de services de proxy ouvert anonyme là-bas.

donc deux approches qui me viennent à l'esprit sont:

  1. demande ajax -- dans un tel cas, le proxy doit utiliser les paramètres CORS appropriés
  2. utilisation d'une iframe -- vous chargez votre script (probablement enveloppé en html) à l'intérieur d'une iframe via le proxy. Une fois le script chargé, il envoie un message à son .parentWindow . Si votre fenêtre a reçu un message, vous pouvez être sûr que le serveur est en cours d'exécution (ou plus précisément était en cours d'exécution une fraction de seconde avant).

si vous êtes seulement intéressé par votre environnement local, vous pouvez essayer d'exécuter chrome avec le drapeau --disable-web-security .


une autre suggestion: avez-vous essayé de charger une image de façon programmatique pour savoir si plus d'information est présente?

11
répondu artur grzesiak 2015-07-12 09:03:43

Check out jQuery.ajaxError () Tiré de référence de: jQuery AJAX Error Handling (HTTP Status Codes) Il saisit les erreurs Ajax globales que vous pouvez gérer de nombreuses façons via HTTP ou HTTPS:

if (jqXHR.status == 501) {
//insecure response
} else if (jqXHR.status == 102) {
//connection refused
}
1
répondu Varshaan 2017-05-23 12:02:29

malheureusement, L'API actuelle de navigateur XHR ne fournit pas d'indication explicite lorsque le navigateur refuse de se connecter en raison d'une "réponse non sécurisée", et aussi lorsqu'il ne fait pas confiance au certificat HTTP/SSL du site web.

mais il y a des moyens pour contourner ce problème.

une solution que j'ai trouvée pour déterminer quand le navigateur ne fait pas confiance au certificat HTTP/SSL, est d'abord de détecter si une erreur XHR s'est produite (en utilisant le jQuery error() callback par exemple), puis vérifier si L'appel XHR est à une URL 'https://', puis vérifier si le XHR readyState est 0, ce qui signifie que la connexion XHR n'a même pas été ouverte (ce qui se produit lorsque le navigateur n'aime pas le certificat).

voici le code où je fais ceci: https://github.com/maratbn/RainbowPayPress/blob/e9e9472a36ced747a0f9e5ca9fa7d96959aeaf8a/rainbowpaypress/js/le_requirejs/public/model_info__transaction_details.js#L88

1
répondu maratbn 2017-03-06 04:39:12

Je ne pense pas qu'il y ait actuellement un moyen de détecter ces messages d'erreur, mais un piratage que vous pouvez faire est d'utiliser un serveur comme nginx devant votre serveur d'application, de sorte que si le serveur d'application est en panne, vous obtiendrez une mauvaise erreur de passerelle de nginx avec le code d'état 502 que vous pouvez détecter dans JS. Sinon, si le certificat n'est pas valide, vous aurez toujours la même erreur générique avec statusCode = 0 .

1
répondu Gaafar 2017-09-10 20:48:47