Xác định xem cuộc gọi ajax không thành công do phản hồi không an toàn hoặc kết nối bị từ chối


115

Tôi đã nghiên cứu rất nhiều và không thể tìm ra cách để xử lý vấn đề này. Tôi đang cố thực hiện lệnh gọi jQuery ajax từ máy chủ https đến máy chủ https locahost đang chạy cầu cảng với chứng chỉ tự ký tùy chỉnh. Vấn đề của tôi là tôi không thể xác định xem phản hồi là kết nối bị từ chối hay phản hồi không an toàn (do thiếu chấp nhận chứng chỉ). Có cách nào để xác định sự khác biệt giữa cả hai kịch bản? Các responseText, và statusCodeluôn là như nhau trong cả hai trường hợp, mặc dù trong chrome console tôi có thể thấy sự khác biệt:

net::ERR_INSECURE_RESPONSE
net::ERR_CONNECTION_REFUSED

responseTextluôn là "" và statusCodeluôn là "0" cho cả hai trường hợp.

Câu hỏi của tôi là, làm thế nào tôi có thể xác định xem một cuộc gọi jQuery ajax không thành công do ERR_INSECURE_RESPONSEhoặc do ERR_CONNECTION_REFUSED?

Sau khi chứng chỉ được chấp nhận, mọi thứ đều hoạt động tốt, nhưng tôi muốn biết liệu máy chủ localhost đã tắt hay nó đang hoạt động nhưng chứng chỉ vẫn chưa được chấp nhận.

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

nhập mô tả hình ảnh ở đây

Ngay cả khi thực hiện thủ công yêu cầu, tôi cũng nhận được kết quả tương tự:

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
tôi sẽ đăng mã của mình, nhưng nó không phải là lỗi mã javascript. Vui lòng đọc kỹ câu hỏi của tôi.
taxicala

1
Hai đối số gọi lại lỗi khác có cung cấp thêm thông tin chi tiết nào không? function (xhr, status, msg) {...Tôi nghi ngờ họ sẽ làm được, nhưng đáng để thử.
Kevin B

2
Không. Từ một máy chủ đến một máy chủ khác. Máy chủ cầu cảng (chạy trong locahost) đã đặt đúng tiêu đề CORS vì khi tôi chấp nhận chứng chỉ thì mọi thứ hoạt động như mong đợi. Tôi muốn xác định xem chứng chỉ đó phải được chấp nhận hay cầu cảng bị hỏng.
taxicala

5
Việc thiếu thông tin từ trình duyệt là hoàn toàn có thể xảy ra. Chỉ đơn giản là một "lỗi" sẽ cung cấp một số lượng thông tin cho một tin tặc được đề xuất, ví dụ. "Hệ thống này có thứ gì đó đang nghe trên cổng, v.v."
Katana314

1
nó không phải là thứ mà tôi muốn, nó là thứ mà tôi cần bởi thiết kế và bản chất của những gì tôi đang phát triển. phía máy chủ không phải là một tùy chọn.
taxicala,

Câu trả lời:


67

Không có cách nào để phân biệt nó với các Trình duyệt Web mới nhất.

Đặc điểm kỹ thuật W3C:

Các bước bên dưới mô tả những gì tác nhân người dùng phải làm cho một yêu cầu đa nguồn gốc đơn giản :

Áp dụng các bước thực hiện yêu cầu và tuân thủ các quy tắc yêu cầu bên dưới khi thực hiện yêu cầu.

Nếu cờ chuyển hướng thủ công không được đặt và phản hồi có mã trạng thái HTTP là 301, 302, 303, 307 hoặc 308 Hãy áp dụng các bước chuyển hướng.

Nếu người dùng cuối hủy yêu cầu Áp dụng các bước hủy bỏ.

Nếu có lỗi mạng Trong trường hợp lỗi DNS, lỗi thương lượng TLS hoặc các loại lỗi mạng khác, hãy áp dụng các bước lỗi mạng . Không yêu cầu bất kỳ loại tương tác người dùng cuối nào.

Lưu ý: Điều này không bao gồm các phản hồi HTTP cho biết một số loại lỗi, chẳng hạn như mã trạng thái HTTP 410.

Nếu không thì Thực hiện kiểm tra chia sẻ tài nguyên. Nếu nó trả về không thành công, hãy áp dụng các bước lỗi mạng. Ngược lại, nếu nó trả về pass, hãy chấm dứt thuật toán này và đặt trạng thái yêu cầu nguồn gốc chéo thành thành công. Không thực sự chấm dứt yêu cầu.

Như bạn có thể đọc, lỗi mạng không bao gồm phản hồi HTTP bao gồm lỗi, đó là lý do tại sao bạn sẽ luôn nhận được 0 dưới dạng mã trạng thái và "" là lỗi.

Nguồn


Lưu ý : Các ví dụ sau được tạo bằng Google Chrome Phiên bản 43.0.2357.130 và dựa trên môi trường mà tôi đã tạo để mô phỏng OP một. Mã cho thiết lập nó nằm ở cuối câu trả lời.


Tôi mặc dù cách tiếp cận Để giải quyết vấn đề này sẽ là thực hiện một yêu cầu phụ qua HTTP thay vì HTTPS như Câu trả lời này nhưng tôi nhớ rằng điều đó là không thể do các phiên bản trình duyệt mới hơn chặn nội dung hỗn hợp.

Điều đó có nghĩa là Trình duyệt web sẽ không cho phép yêu cầu qua HTTP nếu bạn đang sử dụng HTTPS và ngược lại.

Điều này đã xảy ra như vậy từ vài năm trước nhưng các phiên bản Trình duyệt Web cũ hơn như Mozilla Firefox dưới phiên bản 23 cho phép nó.

Bằng chứng về nó:

Đưa ra yêu cầu HTTP từ bảng điều khiển Web Broser của HTTPS usign

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

sẽ dẫn đến lỗi sau:

Nội dung hỗn hợp: Trang tại ' https: // localhost: 8000 / ' đã được tải qua HTTPS, nhưng đã yêu cầu điểm cuối XMLHttpRequest không an toàn ' http: // localhost: 8001 / '. Yêu cầu này đã bị chặn; nội dung phải được phân phát qua HTTPS.

Lỗi tương tự sẽ xuất hiện trong bảng điều khiển của trình duyệt nếu bạn cố gắng thực hiện việc này theo các cách khác như thêm Iframe.

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

Sử dụng kết nối Socket cũng đã được Đăng như một câu trả lời , tôi khá chắc chắn rằng kết quả sẽ giống nhau / tương tự nhưng tôi đã thử.

Cố gắng Mở kết nối ổ cắm từ Web Broswer bằng HTTPS đến điểm cuối ổ cắm không Bảo mật sẽ kết thúc bằng lỗi nội dung hỗn hợp.

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

1) Nội dung hỗn hợp: Trang tại ' https: // localhost: 8000 / ' đã được tải qua HTTPS, nhưng đã cố kết nối với điểm cuối WebSocket không an toàn 'ws: // localhost: 8001 /'. Yêu cầu này đã bị chặn; điểm cuối này phải có sẵn trên WSS.

2) Không có DOMException: Không tạo được 'WebSocket': Kết nối WebSocket không an toàn có thể không được khởi tạo từ một trang được tải qua HTTPS.

Sau đó, tôi cũng đã cố gắng kết nối với một điểm cuối wss, hãy xem Nếu tôi có thể đọc một số thông tin về lỗi kết nối mạng:

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

Thực thi đoạn mã ở trên với Máy chủ bị tắt dẫn đến:

Kết nối WebSocket với 'wss: // localhost: 8001 /' không thành công: Lỗi khi thiết lập kết nối: net :: ERR_CONNECTION_REFUSED

Thực thi đoạn mã ở trên với Máy chủ được bật

Kết nối WebSocket với 'wss: // localhost: 8001 /' không thành công: Bắt tay mở WebSocket đã bị hủy

Nhưng một lần nữa, lỗi mà "chức năng onerror" xuất ra bảng điều khiển không có bất kỳ mẹo nào để phân biệt lỗi này với lỗi khác.


Sử dụng proxy như gợi ý câu trả lời này có thể hoạt động nhưng chỉ khi máy chủ "đích" có quyền truy cập công khai.

Đây không phải là trường hợp ở đây, vì vậy việc cố gắng triển khai một proxy trong trường hợp này sẽ dẫn Chúng ta đến cùng một vấn đề.

Mã để tạo máy chủ HTTPS Node.js :

Tôi đã tạo hai máy chủ HTTPS của Nodejs, sử dụng chứng chỉ tự ký:

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

Để làm cho nó hoạt động, bạn cần phải Cài đặt Nodejs, Cần tạo các chứng chỉ riêng biệt cho từng máy chủ và lưu trữ nó trong các thư mục certs và certs2 tương ứng.

Để chạy nó chỉ cần thực thi node applicationServer.jsnode targetServer.jstrong một thiết bị đầu cuối (ví dụ ubuntu).


6
Đây là câu trả lời đầy đủ nhất cho đến nay. Nó cho thấy rằng bạn đã thực sự thực hiện một số công việc nghiên cứu và thử nghiệm. Tôi thấy bạn đã thử mọi cách có thể để thực hiện điều này và tất cả các tình huống đều thực sự được giải thích rõ ràng. Bạn cũng đã cung cấp mã ví dụ để nhanh chóng thiết lập môi trường thử nghiệm! Công việc thực sự tốt! Cảm ơn cho cái nhìn sâu sắc!
taxicala

Câu trả lời xuất sắc! Tuy nhiên, tôi muốn chỉ ra rằng các chứng chỉ tự ký sẽ vẫn tạo ra lỗi từ trình duyệt nếu chúng từ các máy chủ riêng biệt. @ecarrizo
FabricioG

Nếu chúng ta đang nói về lỗi mạng, Có thể Bạn có thể thấy lỗi trong bảng điều khiển, Theo cách thủ công. nhưng bạn không thể truy cập nó từ mã trong một môi trường an toàn.
ecarrizo

32

Hiện tại: Không có cách nào để phân biệt sự kiện này giữa những người nộp đơn. Vì các trình duyệt không cung cấp sự kiện cho các nhà phát triển truy cập. (Tháng 7 năm 2015)

Câu trả lời này chỉ tìm cách cung cấp ý tưởng cho một giải pháp hacky, bạch tạng và chưa hoàn thiện, tiềm năng.


Tuyên bố từ chối trách nhiệm: câu trả lời này không đầy đủ vì nó không giải quyết hoàn toàn các vấn đề của OP (do chính sách xuất xứ chéo). Tuy nhiên, bản thân ý tưởng này có một số điểm đáng khen mà còn được mở rộng thêm bởi: @artur grzesiak ở đây , sử dụng proxy và ajax.


Sau khi tự mình nghiên cứu khá kỹ, dường như không có bất kỳ hình thức kiểm tra lỗi nào để tìm ra sự khác biệt giữa kết nối bị từ chối và phản hồi không an toàn, ít nhất là khi javascript cung cấp phản hồi cho sự khác biệt giữa hai.

Sự đồng thuận chung trong nghiên cứu của tôi là chứng chỉ SSL được xử lý bởi trình duyệt, vì vậy cho đến khi chứng chỉ tự ký được người dùng chấp nhận, trình duyệt sẽ khóa tất cả các yêu cầu, bao gồm cả những yêu cầu đối với mã trạng thái. Trình duyệt có thể (nếu được mã hóa thành) gửi lại mã trạng thái của chính nó để có phản hồi không an toàn, nhưng điều đó không thực sự giúp ích được gì và thậm chí sau đó, bạn sẽ gặp vấn đề với tính tương thích của trình duyệt (chrome / firefox / IE có các tiêu chuẩn khác nhau) .. một lần nữa)

Vì câu hỏi ban đầu của bạn là để kiểm tra trạng thái của máy chủ của bạn giữa việc đang hoạt động và có một chứng chỉ không được chấp nhận, bạn có thể không thực hiện một yêu cầu HTTP tiêu chuẩn như vậy không?

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

Được cấp, điều này yêu cầu thêm một yêu cầu để xác minh kết nối máy chủ, nhưng nó sẽ hoàn thành công việc bằng quá trình loại bỏ. Mặc dù vậy, tôi vẫn cảm thấy khó hiểu và tôi không phải là một fan hâm mộ lớn của yêu cầu gấp đôi.


7
Tôi đã đọc toàn bộ câu trả lời và tôi không nghĩ điều này sẽ hiệu quả. Cả hai máy chủ của tôi đang chạy HTTPS, có nghĩa là vào lúc này tôi cháy một yêu cầu đến một máy chủ HTTP, trình duyệt sẽ hủy bỏ nó ngay lập tức, bởi vì Bạn không thể làm cho ajax yêu cầu từ một HTTPS server tới một máy chủ HTTP
taxicala

11

Câu trả lời của @ Schultzie khá gần gũi, nhưng rõ ràng http- nói chung - sẽ không hoạt động httpstrong môi trường trình duyệt.

Những gì bạn có thể làm là sử dụng một máy chủ trung gian (proxy) để thay mặt bạn thực hiện yêu cầu. Proxy phải cho phép chuyển tiếp httpyêu cầu từ httpsnguồn gốc hoặc tải nội dung từ nguồn gốc tự ký .

Việc có máy chủ của riêng bạn với chứng chỉ thích hợp có thể là quá mức cần thiết trong trường hợp của bạn - vì bạn có thể sử dụng cài đặt này thay vì máy có chứng chỉ tự ký - nhưng có rất nhiều dịch vụ proxy mở ẩn danh ngoài đó.

Vì vậy, hai cách tiếp cận mà tôi nghĩ đến là:

  1. yêu cầu ajax - trong trường hợp này, proxy phải sử dụng cài đặt CORS thích hợp
  2. sử dụng iframe - bạn tải tập lệnh của mình (có thể được gói trong html) bên trong iframe thông qua proxy. Sau khi tập lệnh được tải, nó sẽ gửi một thông báo tới nó .parentWindow. Nếu cửa sổ của bạn nhận được một thông báo, bạn có thể chắc chắn rằng máy chủ đang chạy (hay chính xác hơn là đang chạy một phần giây trước đó).

Nếu bạn chỉ quan tâm đến môi trường cục bộ của mình, bạn có thể thử chạy chrome với --disable-web-securitycờ.


Một gợi ý khác: bạn đã thử tải một hình ảnh theo chương trình để tìm xem có thêm thông tin ở đó không?


1

Kiểm tra jQuery.ajaxError () Lấy tham chiếu từ: jQuery Xử lý lỗi AJAX (Mã trạng thái HTTP) Nó bắt các lỗi Ajax toàn cầu mà bạn có thể xử lý theo bất kỳ cách nào qua HTTP hoặc HTTPS:

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

Điều này cũng giống như thực hiện ajax theo cách tôi đang làm, nhưng tập trung các phản hồi lỗi vào một nơi.
taxicala

1
Vấn đề là chứng chỉ không được tải khi lệnh gọi ajax được thực hiện vì nó có vẻ là vấn đề. cách nào GL với việc tìm kiếm câu trả lời :)
Varshaan

2
Không, vấn đề là tôi muốn xác định sự khác biệt giữa việc phải chấp nhận chứng chỉ và biết liệu máy chủ có bị tắt hay không.
taxicala

1
Cả khi kết nối bị từ chối và chứng chỉ không được tin cậy, tôi nhận được cùng một jqXHR.status = 0 (và jqXHR.readyState = 0), không phải qXHR.status == 102 hoặc 501 như được mô tả trong câu trả lời này.
anre 22/02/17

1

Rất tiếc, API XHR của trình duyệt ngày nay không cung cấp dấu hiệu rõ ràng về thời điểm trình duyệt từ chối kết nối do "phản hồi không an toàn" và cũng như khi trình duyệt không tin tưởng chứng chỉ HTTP / SSL của trang web.

Nhưng có nhiều cách để giải quyết vấn đề này.

Một giải pháp mà tôi đã đưa ra để xác định khi trình duyệt không tin tưởng chứng chỉ HTTP / SSL, trước tiên là phát hiện xem có lỗi XHR đã xảy ra hay không ( error()ví dụ: sử dụng lệnh gọi lại jQuery ), sau đó kiểm tra xem lệnh gọi XHR có phải là 'https không : // 'URL, sau đó kiểm tra xem XHR readyStatecó phải là 0 hay không, có nghĩa là kết nối XHR thậm chí chưa được mở (đó là điều xảy ra khi trình duyệt không thích chứng chỉ).

Đây là mã nơi tôi thực hiện việc này: https://github.com/maratbn/RainbowPayPress/blob/e9e9472a36ced747a0f9e5ca9fa7d96959aeaf8a/rainbowpaypress/js/le_requirejs/public/model_info__transaction_details.js#L88


1

Tôi không nghĩ rằng hiện tại có cách để phát hiện những thông báo lỗi này, nhưng một cách hack mà bạn có thể làm là sử dụng một máy chủ như nginx phía trước máy chủ ứng dụng của bạn, vì vậy nếu máy chủ ứng dụng không hoạt động, bạn sẽ gặp sự cố. lỗi cổng từ nginx với 502 mã trạng thái mà bạn có thể phát hiện trong JS. Ngược lại, nếu chứng chỉ không hợp lệ, bạn sẽ vẫn gặp phải lỗi chung statusCode = 0.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.