73

不正なリクエストではなく、クロス オリジン エラーが原因で XMLHttpRequest() が失敗するタイミングを検出しようとしています。例えば:

    ajaxObj=new XMLHttpRequest()
    ajaxObj.open("GET", url, true); 
    ajaxObj.send(null);

URL の 4 つのケースを考えてみましょう:

ケース 1: url は access-control-allow-origin が適切に設定されている有効なアドレスです

  • 例:ヘッダーhttp://192.168.8.35に設定されたサーバーがある場合Access-Control-Allow-Origin: *
  • これは、ajaxObj.readyState==4 および ajaxObj.status==200 として簡単に検出できます。

ケース 2: URL が既存のサーバーで無効なアドレスである

  • 例:http://xyz.google.comサーバーは応答するが、有効な要求ではない場合
  • これにより、 ajaxObj.readyState==4 および ajaxObj.status==0 になります

ケース 3: URL が存在しないサーバーの IP アドレスに対するものである

  • 例:http://192.168.8.6応答するものが何もないローカル ネットワーク
  • これにより、 ajaxObj.readyState==4 および ajaxObj.status==0 になります

ケース 4: url は、access-control-allow-origin が設定されていない有効なアドレスです

  • 例:ヘッダーに設定されていないhttp://192.168.8.247サーバーがある場合 Access-Control-Allow-Origin: *
  • これにより、 ajaxObj.readyState==4 および ajaxObj.status==0 になります

問題は、ケース 4 (access-control-allow-origin エラー) とケース 2 と 3 をどのように区別すればよいかということです。

ケース 4 では、Chrome デバッグ コンソールに次のエラーが表示されます。

XMLHttpRequest cannot load http://192.168.8.247/. Origin http://localhost is not allowed by Access-Control-Allow-Origin.

Javascriptでそのエラーを知らせるにはどうすればよいですか?

いくつかの兆候を見つけようとしましajaxObjたが、ケース 2 と 3 に比べて違いはないようです。

これが私が使用した簡単なテストです:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>CORS Test</title>
<script type="text/javascript">
function PgBoot()
{
//  doCORS("http://192.168.8.35");   // Case 1
//  doCORS("http://xyz.google.com"); // Case 2
    doCORS("http://192.168.8.6");    // Case 3
//  doCORS("http://192.168.8.247");  // Case 4
}

function doCORS(url)
{
    document.getElementById("statusDiv").innerHTML+="Processing url="+url+"<br>";
    var ajaxObj=new XMLHttpRequest();
    ajaxObj.overrideMimeType('text/xml');
    ajaxObj.onreadystatechange = function()
    {
        var stat=document.getElementById("statusDiv");
        stat.innerHTML+="readyState="+ajaxObj.readyState;
        if(ajaxObj.readyState==4)
            stat.innerHTML+=", status="+ajaxObj.status;
        stat.innerHTML+="<br>";
    }
    ajaxObj.open("GET", url, true); 
    ajaxObj.send(null);
}
</script>
</head>
<body onload="PgBoot()">
<div id="statusDiv"></div>
</body>
</html>

Chrome を使用した結果:

Processing url=http://192.168.8.35
readyState=1
readyState=2
readyState=3
readyState=4, status=200

Processing url=http://xyz.google.com
readyState=1
readyState=4, status=0

Processing url=http://192.168.8.6
readyState=1
readyState=4, status=0

Processing url=http://192.168.8.247
readyState=1
readyState=4, status=0
4

5 に答える 5

58

いいえ、W3C 仕様によると、違いを見分ける方法はありません。

CORS 仕様が単純なクロスオリジン要求手順をどのように指定しているかを次に示します。

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

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

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

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

それ以外の場合:リソース共有チェックを実行します。失敗が返された場合は、ネットワーク エラーの手順を適用します...

ネットワーク接続の失敗または CORS 交換の失敗の場合、ネットワーク エラーの手順が適用されるため、文字通り 2 つのケースを区別する方法はありません。

なんで?利点の 1 つは、攻撃者が LAN のネットワーク トポロジを検査できないようにすることです。たとえば、悪意のある Web ページ スクリプトは、HTTP インターフェースを要求してルーターの IP アドレスを見つけ、ネットワーク トポロジに関するいくつかの情報 (プライベート IP ブロックの大きさ/8など/16) を知ることができます。ルーターは CORS ヘッダーを送信しない (送信すべきでない) ため、スクリプトはまったく何も学習しません。

于 2013-10-11T19:22:28.857 に答える
17

多分それが誰かを助ける場合に備えて... corsとネットワークエラーの違いを処理するもう1つの方法... chromeまたはfirefoxで動作します...(ただし、完全な解決策ではありません)

var external = 'your url';

if (window.fetch) {
    // must be chrome or firefox which have native fetch
    fetch(external, {'mode':'no-cors'})
        .then(function () {
            // external is reachable; but failed due to cors
            // fetch will pass though if it's a cors error
        })
        .catch(function () {
            // external is _not_ reachable
        });
} else {
    // must be non-updated safari or older IE...
    // I don't know how to find error type in this case
}
于 2016-11-18T18:25:22.880 に答える
7

CORS 違反を他の失敗した AJAX 要求と区別するために、サーバー側コードを使用して HEAD 要求の応答ヘッダーを検査し、結果をクライアント ページに返すことができます。たとえば、AJAX リクエストが失敗した場合 (ステータス 0)、このスクリプトを呼び出して (これを と呼びましょうcors.php)、応答ヘッダーにヘッダーが含まれているかどうかを確実に知ることができますAccess-Control-*

例:

cors.php?url=http://ip.jsontest.com
cors.php?url=http://www.google.com
cors.php?url=http://10.0.0.1

戻り値

HTTP/1.1 200 OK Access-Control-Allow-Origin: *
HTTP/1.1 302 Found
無効なリクエスト


cors.php - 必要に応じてカスタマイズ

<?php /* cors.php */
$url = $_GET["url"];
if(isset($url)) {
    $headers = getHeaders($url);
    header("Access-Control-Allow-Origin: *");

    if(count($headers) == 0) {
        die("Invalid request"); // cURL returns no headers on bad urls
    } else {
        echo $headers[0];       // echo the HTTP status code
    }

    // Include any CORS headers
    foreach($headers as $header) {
        if(strpos($header, "Access-Control") !== false) {
            echo " " . $header;
        }
    }
}

function getHeaders($url, $needle = false) {
    $headers = array();
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 4);        // Timeout in seconds
    curl_setopt($ch, CURLOPT_TIMEOUT, 4);               // Timeout in seconds
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_VERBOSE, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'HEAD');    // HEAD request only
    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $header) use(&$headers) {
        array_push($headers, $header);
        return strlen($header);
    });
    curl_exec($ch);
    return $headers;
} /* Drakes, 2015 */

クライアント側のテスト ハーネス:

function testCORS(url, $elem) {
    $.ajax({
      url: url,
      timeout: 4000
    })
    .fail(function(jqXHR, textStatus) {
       if(jqXHR.status === 0) {
           // Determine if this was a CORS violation or not
           $.ajax({
              context: url,
              url: "http://myserver.com/cors.php?url=" + escape(this.url),
           })
           .done(function(msg) {
              if(msg.indexOf("HTTP") < 0) {
                $elem.text(url + " - doesn't exist or timed out");
              } else if(msg.indexOf("Access-Control-Allow-Origin") >= 0) {
                $elem.text(url + " - CORS violation because '" + msg + "'");
              } else {
                $elem.text(url + " - no Access-Control-Allow-Origin header set");
              }
           });
       } else {
           // Some other failure (e.g. 404), but not CORS-related
           $elem.text(url + " - failed because '" + responseText + "'");
       }
    })
    .done(function(msg) {
      // Successful ajax request
      $elem.text(this.url + " - OK");
    }); /* Drakes, 2015 */
}

ハーネスドライバー:

// Create a div and append the results of the URL calls
$div = $("<div>"); 
$("body").append($div);

var urls = ["http://ip.jsontest.com", "http://google.com", "http://10.0.0.1"];
urls.map( function(url) {
   testCORS(url, $div.append("<h4>").children().last());
});

結果:

http://ip.jsontest.com - OK

http://google.com - Access-Control-Allow-Origin ヘッダーが設定されていません

http://10.0.0.1 - 存在しないか、タイムアウトしました

于 2015-05-02T15:35:10.383 に答える