jak ominąć Access-Control-Allow-Origin?

196

Wykonuję wywołanie ajax do mojego własnego serwera na platformie, którą ustawili, aby zapobiec tym wywołaniom ajax (ale potrzebuję go, aby pobrać dane z mojego serwera, aby wyświetlić pobrane dane z bazy danych mojego serwera). Mój skrypt ajax działa, może wysyłać dane do skryptu php mojego serwera, aby umożliwić jego przetwarzanie. Nie może jednak odzyskać przetworzonych danych, ponieważ są one blokowane przez"Access-Control-Allow-Origin"

Nie mam dostępu do źródła / rdzenia tej platformy. więc nie mogę usunąć skryptu, który nie pozwala mi tego zrobić. (P / SI użył konsoli Google Chrome i odkrył ten błąd)

Kod Ajax, jak pokazano poniżej:

 $.ajax({
     type: "GET",
     url: "http://example.com/retrieve.php",
     data: "id=" + id + "&url=" + url,
     dataType: 'json',   
     cache: false,
     success: function(data)
      {
        var friend = data[1];              
        var blog = data[2];           
        $('#user').html("<b>Friends: </b>"+friend+"<b><br> Blogs: </b>"+blog);

      } 
  });

lub czy istnieje JSONkod równoważny powyższemu skryptowi ajax? Myślę, że JSONjest dozwolone.

Mam nadzieję, że ktoś może mi pomóc.

ETAN
źródło
wszystkie odpowiedzi na twoje pytanie wyjaśniły sposób na przepisanie kodu serwera, abyś mógł działać. Żadne z nich nie polega na omijaniu, o co konkretnie pytałeś w swoim pytaniu. Czy udało ci się ominąć ten nagłówek? Naprawdę wątpię, by taki był.
Moradnejad
nie ma sposobu, aby to pominąć. ale możesz umieścić plik na backend, który wykonuje żądanie. Więc wywołujesz plik ajax na własnym serwerze, plik ten ładuje dane z retrieve.php i odsyła je z powrotem do twojego javascript. W takim przypadku nie ma blokujących Cię reguł CORS.
Jona Paulus

Odpowiedzi:

365

Umieść to na górze retrieve.php:

header('Access-Control-Allow-Origin: *');  

Pamiętaj, że to skutecznie wyłącza ochronę CORS i naraża użytkowników na atak. Jeśli nie masz całkowitej pewności, że musisz zezwolić na wszystkie źródła, powinieneś zablokować to do bardziej konkretnego źródła:

header('Access-Control-Allow-Origin: https://www.example.com')

Proszę zapoznać się z poniższą odpowiedzią stosu dla lepszego zrozumienia Access-Control-Allow-Origin

https://stackoverflow.com/a/10636765/413670

Rafay
źródło
54
To raczej niebezpieczne. Sprawdź moją odpowiedź na dole.
Rob
3
tnx, ale nie powinieneś zezwalać na dostęp do wszystkich źródeł, o których wspomniał @RobQuist w swoim komentarzu, a w jego odpowiedzi zapewniono lepsze podejście
Rafay
2
Znalazłem więc tę stronę, ponieważ musiałem ominąć kontrolę dostępu na serwerze. Rozwiązaniem tutaj nie jest ominięcie niczego, ale po prostu prawidłowe skonfigurowanie kontroli dostępu na własnym serwerze. W przypadku, gdy ktokolwiek naprawdę musi to ominąć, może użyć PHP file_get_contents ($ remote_url) ;. Istnieje oczywiście wiele sposobów, aby to zrobić, ale tak to zrobiłem.
Shawn Whinnery
1
@ShawnWhinnery, który jest w zasadzie aktem „pośrednictwa”. Dobre rozwiązanie, jeśli naprawdę chcesz dynamicznie ładować dane z innej witryny, nad którą nie masz kontroli.
Rob
1
chciałem uruchomić skrypt PHP z rdzenia dotnet - przeniosłem skrypt php na mój inny adres URL, ale otrzymywałem błąd skryptu między witrynami. dodałem kod, który pokazałeś na początku PHP i działał idealnie. Dzięki!
raddevus
290

Dobrze, ale wszyscy wiecie, że * jest symbolem wieloznacznym i umożliwia wykonywanie skryptów między witrynami z każdej domeny?

Chcesz wysłać wiele Access-Control-Allow-Originnagłówków dla każdej witryny, która może to zrobić - ale niestety oficjalnie nie jest obsługiwane wysyłanie wielu Access-Control-Allow-Originnagłówków lub tworzenie wielu źródeł.

Możesz rozwiązać ten problem, sprawdzając źródło i odsyłając go w nagłówku, jeśli jest to dozwolone:

$origin = $_SERVER['HTTP_ORIGIN'];
$allowed_domains = [
    'http://mysite1.com',
    'https://www.mysite2.com',
    'http://www.mysite2.com',
];

if (in_array($origin, $allowed_domains)) {
    header('Access-Control-Allow-Origin: ' . $origin);
}

To o wiele bezpieczniejsze. Możesz edytować dopasowanie i zmienić je na funkcję ręczną z pewnym wyrażeniem regularnym lub czymś takim. Przynajmniej odeśle to tylko 1 nagłówek, a będziesz pewien, że to ten, z którego pochodzi żądanie. Pamiętaj, że wszystkie nagłówki HTTP mogą być sfałszowane, ale ten nagłówek służy ochronie klienta. Nie chroń swoich danych za pomocą tych wartości. Jeśli chcesz dowiedzieć się więcej, przeczytaj trochę o CORS i CSRF.

Dlaczego jest bezpieczniej?

Zezwalanie na dostęp z innych lokalizacji niż Twoja zaufana witryna umożliwia przechwytywanie sesji. Idę z małym przykładem - obraz Facebook pozwala na wieloznaczne pochodzenie - oznacza to, że możesz gdzieś stworzyć własną stronę internetową i uruchamiać połączenia AJAX (lub otwarte iframe) na Facebooku. Oznacza to, że możesz pobrać zalogowane informacje na Facebooku osoby odwiedzającej Twoją witrynę. Co gorsza - możesz skryptować POSTżądania i publikować dane na czyimś facebooku - tylko podczas przeglądania Twojej witryny.

Zachowaj ostrożność podczas używania ACAOnagłówków!

Obrabować
źródło
12
Myślę, że musisz umieścić http: // przed każdym elementem na liście. Przynajmniej zrobiłem dla jednej strony, nad którą pracowałem.
blak3r
2
Niestety to nie działa. Uważam, że na każde wywołanie funkcji header () można podać tylko jeden wyjątek.
lewsid
5
@Shanimal & lewsid -> Wydaje mi się, że oddzielone przecinkiem nie działa. Odniesienie: w3.org/TR/cors
Rob
3
Aby poradzić sobie z listą domen, tutaj odpowiednia odpowiedź: stackoverflow.com/a/1850482/766177
Valentin Despa
13
Nie ma sensu dodawanie 4 takich nagłówków, ponieważ każde wywołanie header()zastępuje poprzedni nagłówek tego samego typu. Więc tak naprawdę wszystko, co robisz, to ustawianie ostatniego nagłówka. Ręczne wprowadzanie stwierdza, że można ustawić drugi parametr false, aby zapobiec poprzedni nagłówek jest nadpisane.
BadHorsie
31

Ostrzeżenie : Chrome (i inne przeglądarki) będą narzekać, że ustawiono wiele nagłówków ACAO, jeśli zastosujesz się do innych odpowiedzi.

Błąd będzie podobny XMLHttpRequest cannot load ____. The 'Access-Control-Allow-Origin' header contains multiple values '____, ____, ____', but only one is allowed. Origin '____' is therefore not allowed access.

Spróbuj tego:

$http_origin = $_SERVER['HTTP_ORIGIN'];

$allowed_domains = array(
  'http://domain1.com',
  'http://domain2.com',
);

if (in_array($http_origin, $allowed_domains))
{  
    header("Access-Control-Allow-Origin: $http_origin");
}

źródło
6
To jest jeszcze lepsze rozwiązanie niż to, które zamieściłem.
Rob
7

Rozwiązałem ten problem podczas wywoływania kontrolera MVC3. Dodałem:

Response.AddHeader("Access-Control-Allow-Origin", "*"); 

przed moim

return Json(model, JsonRequestBehavior.AllowGet);

A także $.ajaxnarzekałem, że nie akceptuje nagłówka Content Type w moim wywołaniu ajax, więc skomentowałem go, ponieważ wiem, że jego JSON jest przekazywany do akcji.

Mam nadzieję, że to pomaga.

Atif Rehman
źródło
2

Jest to naprawdę zły pomysł *, który sprawia, że ​​jesteś otwarty na skrypty między witrynami. Zasadniczo chcesz mieć własną domenę przez cały czas, z uwzględnieniem aktualnych ustawień SSL i opcjonalnie dodatkowych domen. Chcesz również, aby wszystkie były wysyłane jako jeden nagłówek. Poniższe elementy zawsze autoryzują Twoją domenę w tym samym zakresie SSL, co bieżąca strona, i opcjonalnie mogą obejmować dowolną liczbę dodatkowych domen. Wyśle je wszystkie jako jeden nagłówek i zastąpi poprzednie, jeśli coś już je wysłało, aby uniknąć jakiejkolwiek możliwości narzekania przeglądarki na wysyłanie wielu nagłówków kontroli dostępu.

class CorsAccessControl
{
    private $allowed = array();

    /**
     * Always adds your own domain with the current ssl settings.
     */
    public function __construct()
    {
        // Add your own domain, with respect to the current SSL settings.
        $this->allowed[] = 'http'
            . ( ( array_key_exists( 'HTTPS', $_SERVER )
                && $_SERVER['HTTPS'] 
                && strtolower( $_SERVER['HTTPS'] ) !== 'off' ) 
                    ? 's' 
                    : null )
            . '://' . $_SERVER['HTTP_HOST'];
    }

    /**
     * Optionally add additional domains. Each is only added one time.
     */
    public function add($domain)
    {
        if ( !in_array( $domain, $this->allowed )
        {
            $this->allowed[] = $domain;
        }
    /**
     * Send 'em all as one header so no browsers grumble about it.
     */
    public function send()
    {
        $domains = implode( ', ', $this->allowed );
        header( 'Access-Control-Allow-Origin: ' . $domains, true ); // We want to send them all as one shot, so replace should be true here.
    }
}

Stosowanie:

$cors = new CorsAccessControl();

// If you are only authorizing your own domain:
$cors->send();

// If you are authorizing multiple domains:
foreach ($domains as $domain)
{
    $cors->add($domain);
}
$cors->send();

Masz pomysł.

mopsyd
źródło
1

Czy próbowałeś rzeczywiście dodać nagłówek Access-Control-Allow-Origin do odpowiedzi wysłanej z twojego serwera? Jak Access-Control-Allow-Origin: *?

Daniel Brockman
źródło
1
Jest to nagłówek HTTP wysyłany przez serwer w celu poinformowania przeglądarki, że ujawnienie wyniku skryptu wywołującemu jest w porządku, mimo że domena skryptu nie jest zgodna z domeną serwera. Przeczytaj więcej na temat udostępniania zasobów między różnymi źródłami !
Daniel Brockman