7 lutego 2020

CORS – co to jest, jak obejść? Teoria i praktyka w AJAX

Kategoria: Bezpieczeństwo
Tagi:
Autor: Paweł Mansfeld

CORS – co to jest, jak obejść? Teoria i praktyka w AJAX

W dużym uproszczeniu, CORS (ang Cross-Origin Resource Sharing) to mechanizm blokujący wykonywanie przeglądarkom asynchronicznych żądań HTTP do zasobów znajdujących się w osobnej domenie – a ściślej zasobów o innym pochodzeniu (ang. Origin).

Origin to protokół, domena i port. Sub-domeny też są traktowane jako osobne domeny.

https://przykladowa-domena.pl/resource.json 
https://przykladowa-domena.pl/katalog/resource.json 

należą do tego samego Origin

natomiast:

 http://przykladowa-domena.pl/resource.json
 https://app.przykladowa-domena.pl/resource.json 

nie należą do tego samego origin i są traktowane na równi z URL związanymi z innymi domenami, np:

 http://jakas-inna-domena.pl/resource.json

Aby to wszystko łatwo wytłumaczyć, zrobimy bardzo prostą aplikację, która będzie pobierać tekst za pomocą AJAX.

Na czym polega ograniczenie CORS?

CORS polega na tym, że domyślnie nie można w aplikacji pod adresem https://przykladowa-domena.pl/ wysłać zapytania AJAXowego do innego Origin.

W rzeczywistości, aplikacje i strony internetowe mogą współdzielić zasoby w inny sposób. Można przecież osadzać:

  • elementy iframe linkujące do innej domeny,
  • osadzać obrazki i wideo zamieszczone na innych stronach (tzw. hotlinkowanie) za pomocą atrybutu src,
  • można dodawać CSS z innej domeny za pomocą atrybutu href,
  • można dodawać pliki JavaScript z innej domeny za pomocą tagu <script>,
  • no i można zamieszczać linki…

Ale przy standardowych ustawieniach serwera nie można wysłać zapytania AJAX do innej domeny (a ściślej: innego Origin). Nie można też:

  • dołączać fontów z innych domen,
  • plików SVG.

Po co zostały wprowadzone ograniczenia CORS?

Choć początkowo polityka CORS może się wydawać absurdalna i w rzeczywistości mało deweloperów ją rozumie, zwalcza ona poważny atak o nazwie CSRF (ang. Cross-site Request Forgery). Jest to często mylony z atakiem XSS atak JavaScript, polegający na tym, że kod wysyła zapytania HTTP do innych serwisów wykorzystując uprawnienia użytkownika np. stan zalogowania, inne dane zapisane w sesji lub w cookie.

Gdyby nie polityka CORS, łatwo byłoby przykładowo umieścić na stronie JavaScript, który:

  • wysyłałby przypadkowym osobom zaproszenia do znajomych… na Facebooku,
  • wylogowywałby nas z innych serwisów,
  • zmieniałby w nim nasze hasło,
  • niewidocznie “nabijałby” ruch internetowy innym stronom,
  • robił zakupy na Allegro itd…

Oczywiście, zakładając że strony te nie miałyby innych mechanizmów zabezpieczających.

CORS w praktyce (na przykładzie PHP i JavaScript)

Stwórzmy minimalną aplikację AJAX wysyłającą zapytanie do pliku PHP zwracającego prosty ciąg znaków. Na początku, w pliku dane.php wyświetlmy prosty i dostępny dla każdego napis:

<?php 
echo "sekret";

Zamieśćmy ten plik na jakimś własnym serwerze lub localhoście. Zróbmy teraz drugi plik index.html i umieśćmy go w tym samym serwerze.

<div id="data">
</div>
<button onClick="getData()">Pobierz dane</button>

<script>
 function getData() {
   var xhttp = new XMLHttpRequest();
   xhttp.onreadystatechange = function() {
     if (this.readyState == 4 && this.status == 200) {
      document.getElementById("data").innerHTML = this.responseText;
     }
   };
   xhttp.open("GET", "https://twoja-domena.pl/dane.php", true);
   xhttp.send();
 }
 </script>

Jak widać wszystko działa. Za pomocą AJAX wysłaliśmy zapytanie do pliku na tym samym serwerze. Plik zwrócił wiadomość i mogliśmy ją bez problemu wykorzystać w bieżącej stronie:

Odpytania danych z tego samego Origin.

Jeżeli teraz wyślemy plik dane.php do innej domeny zmieniając w kodzie ścieżkę:

xhttp.open("GET", "https://twoja-inna-domena.pl/dane.php", true);

Nasz aplikacja nie zadziała, wyświetli się błąd:

Access to XMLHttpRequest at… from origin…. has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

Błąd w Google Chrome z powodu blokady CORS.

Jak umożliwić komunikację AJAX pomiędzy aplikacjami?

Sprawa jest bardzo prosta, wystarczy skonfigurować serwer przechowujący plik dane.php tak, aby wysyłał specjalny nagłówek:

Header set Access-Control-Allow-Origin “*”

W serwerach Apache do pliku .htaccess wystarczy dodać:

<IfModule mod_headers.c>
<FilesMatch ".(ttf|ttc|otf|eot|woff|font.css|css|js|php)$">
Header set Access-Control-Allow-Origin "*"
</FilesMatch>
</IfModule>

W filtrze files match można sobie dostosować, dla jakich plików nagłówek na być zastosowywany. Po dodaniu takiej instrukcji do .htaccess będzie możliwe odpytanie zasobów dostępnych w innym origin w naszej aplikacji:

Udane zapytanie dzięki skonfigurowaniu nagłówka Access-Control-Allow-Origin “*”

Oczywiście zamiast gwiazdki możemy wpisać domeny, które mają mieć ekskluzywny dostęp do tych danych, wygląda to wówczas tak:

Access-Control-Allow-Origin: "https://przykladowa-domena.pl"

Jak obejść CORS jeżeli nie mamy dostępu do konfiguracji serwera?

Czasem musimy pobrać aktualizowane “na żywo” dane znajdujące się na zewnętrznym serwerze. Niestety, deweloperzy po drugiej stronie udostępniający plik JSON czy XML nie mają pojęcia o ograniczeniu CORS. Jak to obejść?

Jeżeli dane są dostępne publicznie, wystarczy stworzyć taki jakby pomostowy plik.php na swoim serwerze, który będzie pobierał te dane i serwował nam je z własnej lokalizacji. Tworzymy drugi plik PHP download.php w tym samym origin co aplikacja:

<?php
echo file_get_contents("https://twoja-inna-domena.pl/dane.php");

W aplikacji JavaScript zmieniamy linijkę:

xhttp.open("GET", "https://twoja-domena.pl/download.php", true);

Nawet nie trzeba sprawdzać czy to działa. Wykorzystując pośredni plik we własnym back-endzie, serwer pobiera za nas dane a aplikacja zgodnie z naszymi zamierzeniami prezentuje je w przeglądarce:

“Ominięcie” ograniczenia CORS za pomocą ściągnięcia danych do back-endu.

Przeglądarka pobrała dane, bo pokazuje zasób z tego samego origin. To, że został on przed chwilą pobrany przez plik pobierz.php z całkiem innej lokalizacji dane.php, w ogóle ją nie interesuje. Dane, które zostały pobrane przez plik pobierz.php to oczywiście treść danych z perspektywy serwera aplikacji, dlatego nie będą one w żaden sposób spersonalizowane dla klienta i nie gra roli czy jest zalogowany do tamtej lokalizacji czy dzieli z nią ciasteczko. Jest to zatem bezpieczne i można tego użyć jeżeli CORS blokuje dostęp do i tak publicznie dostępnych danych.

Wtyczki wyłączające CORS

Jako klient możemy wyłączyć CORS we własnej przeglądarce za pomocą wtyczek. Jest to przydatne podczas prac deweloperskich:

Chrome:

https://chrome.google.com/webstore/detail/moesif-orign-cors-changer/digfbfaphojjndkpccljibejjbppifbc

Firefox:

https://addons.mozilla.org/pl/firefox/addon/cors-everywhere/

Podsumowanie

Jak widać polityka, CORS to potrzebne zabezpieczenie, które chroni nas przed wysyłaniem zapytań HTTP za pomocą AJAX na innych stronach. Z perspektywy deweloperów, CORS może być nie raz przeszkodą ale umiejętność skonfigurowania nagłówków Access-Control-Allow-Origin oraz możliwość pobrania danych przez back-end aplikacji pozwoli nam wybrnąć z każdej sytuacji.

Źródła

https://enable-cors.org/

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

Oceń artykuł na temat: CORS – co to jest, jak obejść? Teoria i praktyka w AJAX
Średnia : 4.8 , Maksymalnie : 5 , Głosów : 8


 

Odpowiedz lub skomentuj

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *




Komentarze

Marcin

20 maja 2020

Wszystko cacy, dopóki nie chcemy na przykład zaciągnąć streama audio ajaxem...

Paweł Mansfeld

22 maja 2020

O, brzmi jak coś czego bym nigdy nie próbował ;)

 

Wykryto brak połączenia z Internetem.