安全でない応答または接続拒否が原因でajax呼び出しが失敗したかどうかを判別します


114

私は多くの研究をしており、これを処理する方法を見つけることができませんでした。カスタムの自己署名証明書を使用して、httpsサーバーからjettyを実行しているlocahost httpsサーバーにjQuery ajax呼び出しを実行しようとしています。私の問題は、応答が接続拒否であるか、または安全でない応答であるか(証明書の受け入れがないため)を判断できないことです。両方のシナリオの違いを判断する方法はありますか?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コードエラーではありません。私の質問をよく読んでください。
taxicala 2015年

1
他の2つのエラーコールバック引数は、追加の洞察を与えますか?function (xhr, status, msg) {...私は彼らがそうすることを疑いますが、試す価値があります。
Kevin B

2
いいえ。サーバーから別のサーバーへ。(cacahostで実行されている)桟橋サーバーはCORSヘッダーを適切に設定しています。これは、証明書を受け入れるとすべてが期待どおりに機能するためです。証明書を受け入れる必要があるか、桟橋がダウンしているかを確認したい。
taxicala 2015年

5
ブラウザからの情報の欠如が完全に意図的なものである可能性は十分にあります。単に「エラー」は、提案されたハッカーにある量の情報を提供します。「このシステムにはポートなどでリッスンしているものがあります。」
Katana314 2015年

1
それは私が望むものではなく、私が開発しているものの性質と性質によって必要なものです。サーバー側はオプションではありません。
taxicala 2015

回答:


67

最新のWebブラウザと区別する方法はありません。

W3C仕様:

以下の手順は、単純なクロスオリジンリクエストに対してユーザーエージェントが実行する必要があることを示しています

リクエストを行う際は、リクエストの作成手順を適用し、以下のリクエストルールを遵守してください。

手動リダイレクトフラグが設定されておらず、応答のHTTPステータスコードが301、302、303、307、または308 の場合リダイレクト手順を適用します。

エンドユーザーがリクエストをキャンセルした場合、 中止手順を適用します。

ネットワークエラーがある 場合DNSエラー、TLSネゴシエーションエラー、またはその他のタイプのネットワークエラーの場合は、ネットワークエラーの手順を適用します。エンドユーザーの操作は一切要求しないでください。

注:これには、HTTPステータスコード410など、ある種のエラーを示すHTTP応答は含まれません。

それ以外の場合は 、リソース共有チェックを実行します。失敗した場合は、ネットワークエラーの手順を適用します。それ以外の場合、合格を返した場合、このアルゴリズムを終了し、クロスオリジンリクエストのステータスを成功に設定します。実際にリクエストを終了しないでください。

ご覧のとおり、ネットワークエラーには、エラーを含むHTTP応答が含まれていません。そのため、ステータスコードとして常に0が返され、エラーとして ""が返されます。

ソース


:次の例は、Google Chromeバージョン43.0.2357.130を使用して、OP 1をエミュレートするために作成した環境に対して作成されました。それを設定するためのコードは答えの一番下にあります。


これを回避するには、HTTPSではなくHTTPを介して2次要求を行うと思いますが、この回答は、新しいバージョンのブラウザーが混合コンテンツをブロックするため不可能であることを覚えています。

つまり、HTTPSを使用している場合、WebブラウザーはHTTPを介した要求を許可しません。逆の場合も同様です。

これは数年前からこのようになっていますが、Mozilla Firefoxなどの古いバージョンのWebブラウザーでは、バージョン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)キャッチされないDOMException: 'WebSocket'の構築に失敗しました:安全でないWebSocket接続がHTTPS経由でロードされたページから開始されない可能性があります。

次に、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関数」がコンソールに出力するエラーには、他のエラーと区別するためのヒントがありません。


この回答が示すようにプロキシを使用することは、「ターゲット」サーバーがパブリックアクセスを持っている場合にのみ機能します。

ここではそうではなかったため、このシナリオでプロキシを実装しようとすると、同じ問題が発生します。

Node.js HTTPSサーバーを作成するコード

自己署名証明書を使用する2つの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は、ターミナルで実行します(ubuntuの例)。


6
これは、これまでで最も完全な答えです。実際にいくつかの調査作業とテストを行ったことを示しています。私はあなたがこれを実行するためにあらゆる可能な方法を試みたと思います、そしてすべてのシナリオは本当によく説明されています。また、テスト環境をすばやくセットアップするためのサンプルコードも提供しています。本当にいい仕事です!洞察をありがとう!
taxicala 2015

正解です。ただし、自己署名証明書が別のサーバーのものである場合、ブラウザーからのエラーが引き続き発生することを指摘しておきます。@ecarrizo
FabricioG

ネットワークエラーについて話している場合は、おそらく手動でコンソールにエラーを表示できます。ただし、安全な環境ではコードからアクセスできません。
ecarrizo

32

現在:このイベントをブラウザー間で区別する方法はありません。ブラウザは開発者がアクセスするイベントを提供しないので。(2015年7月)

この回答は、潜在的なアルビエットハックで不完全なソリューションのアイデアを提供することを目的としています。


免責事項: OPの問題を完全に解決するわけではないため、この回答は不完全です(クロスオリジンポリシーのため)。ただし、アイデア自体には、プロキシとajaxを使用した@artur grzesiak hereによってさらに拡張されるいくつかのメリットがあります。


私自身、かなりの調査を行った結果、接続拒否と安全でない応答の違いについて、少なくともJavaScriptが2つの違いの応答を提供する限り、エラーチェックの形式はないようです。

私の研究の一般的なコンセンサスは、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();

これにはサーバーの接続を確認するための追加の要求が必要ですが、排除のプロセスによってジョブを完了する必要があります。それでもまだハッキリしているように感じます。私は2倍のリクエストの大ファンではありません。


7
私は答え全体を読みましたが、これがうまくいくとは思いません。あなたは、HTTPサーバへのHTTPSサーバーからAJAX要求を行うことができないので、両方の私のサーバーは、現時点では私はHTTPサーバに要求を発射することを意味し、HTTPSを実行している、ブラウザは、すぐにそれをキャンセルさせていただきます
taxicala

11

@Schultzieの答えはかなり近いですが、明らかにhttp-一般的には- httpsブラウザー環境では機能しません。

ただし、中間サーバー(プロキシ)を使用して、ユーザーに代わって要求を行うことができます。プロキシはhttphttpsオリジンからのリクエストの転送、または自己署名オリジンからのコンテンツのロードを許可する必要があります。

自己署名証明書を備えたマシンの代わりにこの設定を使用できるため、適切な証明書を備えた独自のサーバーを用意することは、おそらくやり過ぎです。しかし、そこには匿名のオープンプロキシサービスがたくさんあります。

私の頭に浮かぶ2つのアプローチは次のとおりです。

  1. ajaxリクエスト -このような場合、プロキシは適切なCORS設定を使用する必要があります
  2. iframeの使用 -プロキシ経由でiframe内にスクリプト(おそらくhtmlでラップされている)をロードします。スクリプトがロードされると、メッセージがに送信されます.parentWindow。ウィンドウがメッセージを受信した場合は、サーバーが実行されていることを確認できます(正確には、以前は1秒の何分の1か実行されていました)。

ローカル環境のみに関心がある場合は、--disable-web-securityフラグを付けてchromeを実行してみてください。


別の提案:より多くの情報がそこに存在するかどうかを確認するために、プログラムで画像をロードしようとしましたか?


1

チェックアウトjQuery.ajaxErrorを() からの基準を撮影:jQueryのAJAXエラー処理(HTTPステータスコード) それはあなたがHTTPまたはHTTPS上で、任意の数の方法で扱うことができるグローバルなAjaxのエラーをキャッチ:

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

これは、私がやっている方法でajaxを行うのと同じですが、エラー応答を1か所に集中させます。
taxicala 2015

1
問題はajax呼び出しが行われたときに証明書がロードされないことです。これは問題のようです。答えを見つける
のにうれしい

2
いいえ、問題は、証明書を受け入れる必要があることと、サーバーがシャットダウンされているかどうかを知ることの違いを判断したいことです。
taxicala

1
この回答に記載されているように、接続が拒否され、証明書が信頼されていない場合、qXHR.status == 102または501ではなく、同じjqXHR.status = 0(およびjqXHR.readyState = 0)を取得します。
anre

1

残念ながら、現在のブラウザのXHR APIは、「安全でない応答」のためにブラウザが接続を拒否する時期、およびWebサイトのHTTP / SSL証明書を信頼しない時期を明示的に示していません。

しかし、この問題を回避する方法はいくつかあります。

ブラウザーがHTTP / SSL証明書を信頼しない場合を判断するために思いついた1つの解決策は、まずXHRエラーが発生したかどうかを検出し(error()たとえばjQuery コールバックを使用して)、次にXHR呼び出しが 'https :// 'URLをクリックし、XHR readyStateが0であるかどうかを確認します。これは、XHR接続が開かれていないことを意味します(ブラウザーが証明書を好まない場合に起こります)。

これを行うコードは次のとおりです


1

現在、これらのエラーメッセージを検出する方法はないと思いますが、アプリケーションサーバーの前でnginxなどのサーバーを使用することで、アプリケーションサーバーがダウンした場合に問題が発生します。502JSで検出できるステータスコードを含むnginxからのゲートウェイエラー。それ以外の場合、証明書が無効でも、と同じ一般的なエラーが発生しstatusCode = 0ます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.