web-dev-qa-db-ja.com

安全でない応答が原因でajax呼び出しが失敗したか、接続が拒否されたかを判断する

私は多くの研究を行ってきましたが、これを処理する方法を見つけることができませんでした。 httpsサーバーから、カスタムの自己署名証明書を使用してjettyを実行しているlocahost httpsサーバーへのjQuery ajax呼び出しを実行しようとしています。私の問題は、応答が拒否された接続であるか、安全でない応答であるかを判別できないことです(証明書の受け入れがないため)。両方のシナリオの違いを判断する方法はありますか? chromeコンソールでは違いがわかりますが、responseTextstatusCodeはどちらの場合も常に同じです。

net::ERR_INSECURE_RESPONSE
net::ERR_CONNECTION_REFUSED

どちらの場合も、responseTextは常に ""であり、statusCodeは常に "0"です。

私の質問は、ERR_INSECURE_RESPONSEが原因でjQuery ajax呼び出しが失敗したのか、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();
114
taxicala

最新の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を介してセカンダリリクエストを行うことですが、新しいバージョンのブラウザが混合コンテンツをブロックするため不可能であることを思い出しました.

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

これは数年前からこのようになっていますが、バージョン23以下のMozilla Firefoxなどの古いWebブラウザバージョンでは許可されています。

それに関する証拠:

HTTPSを使用する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ブラウザーから非セキュアソケットエンドポイントへのソケット接続を開こうとすると、混合コンテンツエラーが発生します。

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

1)混合コンテンツ:「 https:// localhost:8000 / 」のページはHTTPS経由でロードされましたが、安全でないWebSocketエンドポイント「ws:// localhost:8001 /」に接続しようとしました。このリクエストはブロックされました。このエンドポイントはWSSを介して利用可能でなければなりません。

2)キャッチされていない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関数」がコンソールに出力するエラーには、他のエラーを区別するためのヒントがありません。


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

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

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の例)。

66
ecarrizo

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

この答えは、潜在的でハッカー的で不完全な解決策のアイデアを提供しようとしているだけです。


免責事項:この回答は不完全です完全にOPの問題を解決しません(クロスするため、発信元ポリシー)。ただし、アイデア自体にはいくつかのメリットがあります:@ artur grzesiak here、プロキシとajaxを使用して。


かなりの時間をかけて自分で調べたところ、接続が拒否された場合と安全でない応答との違いについては、少なくとも2つの違いに対する応答を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();

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

31
acupajoe

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

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

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

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

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

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


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

11
artur grzesiak

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

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

ブラウザがHTTP/SSL証明書を信頼しない場合を判断するために思いついた解決策の1つは、まず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 Error Handling(HTTP Status Codes) 処理可能なグローバルAjaxエラーをキャッチしますHTTPまたはHTTPSを介したさまざまな方法:

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