Co to jest CORS (Cross-Origin Resource Sharing)? Jak działa? Jak chroni strony WWW i klientów przeglądarek internetowych? Jak wdrożyć?
Spis treściCo to jest CORS i na czym polega jego działanie? Rozwiązanie Cross-Origin Resource Sharing ma bezpośredni wpływ na bezpieczeństwo klientów przeglądarek internetowych. Trzeba natomiast wiedzieć, jak go prawidłowo wdrożyć i na czym polega jego zadanie w praktyce.
Mechanizm CORS odgrywa niezwykle istotną rolę w kwestii bezpieczeństwa i ochrony przeglądarek internetowych. Dzięki niemu wywoływana zawartość przedstawiana jest wyłącznie tym klientom, którzy zostaną uznani za zaufanych. W ten sposób możliwe jest uniknięcie licznych oszustw i ataków sieciowych. Nie każdy internauta jednak zdaje sobie sprawę z istnienia tego rodzaju rozwiązania.
CORS balansuje na granicy frontendu i backendu. Można powiedzieć, że stanowi pewnego rodzaju rozwinięcie podstawowego mechanizmu obronnego SOP. Na pierwszy rzut oka wydawać się może pewną „luką” w zabezpieczeniach. Pozornie rozluźnia rygorystyczną politykę, chroniącą bezpieczeństwo WWW. W praktyce jednak pozwala na sprawne korzystanie z internetu w takiej formie, jaką wszyscy znamy. Jednocześnie minimalizuje zagrożenie, korzystając z zaawansowanych i sprawdzonych rozwiązań technologicznych.
Co to jest CORS?
CORS (Cross-Origin Resource Sharing) jest zaawansowanym mechanizmem bezpieczeństwa stron WWW. Jego rolą jest przekazanie przeglądarce informacji, czy klientowi powinny zostać udostępnione dane, których dotyczyło jego żądanie. Odbywa się to za pomocą dodatkowych nagłówków HTTP. Co istotne, wystosowane zapytanie i tak wykonywane jest poprawnie. Nawet w przypadku braku zgody na zwrot danych odpowiedź serwera nadal jest generowana. Od CORS zależy natomiast, czy konkretne dane zostaną przedstawione.
Serwer ocenia, czy konkretny klient może zostać uznany za zaufanego. Następnie przeglądarka otrzymuje odpowiednie dane, zależne od werdyktu. Na ich podstawie dowiaduje się, czy ma udostępnić pożądane informacje. W ten sposób CORS umożliwia bezpieczny dostęp do zasobów między różnymi źródłami.
Co „techniczny” opis i definicja CORS oznaczają w praktyce? Najprościej mówiąc, bez tego rozwiązania strona internetowa nie mogłaby ładować zasobów pochodzących z innych domen. Byłoby to niewykonalne, jeśli nie zostałaby wyrażona na to zgoda. Co istotne, związane jest to z funkcjonowaniem wielu niezbędnych elementów witryn. Mowa tu m.in. o stylach CSS, skryptach JavaScript czy nawet obrazach. Bez opisywanego mechanizmu wszelkie próby ładowania takich zawartości byłyby blokowane i uznawane za potencjalne niebezpieczeństwo. Wynika to bezpośrednio z polityki SOP i różnych originów.
Origin i SOP (Same Origin Policy) a CORS
Aby jeszcze lepiej wyjaśnić sens działania CORS, przyjrzyjmy się mechanizmowi SOP i związanym z nim pojęciu origin. Ten ostatni służy do definiowania pojedynczej aplikacji czy serwisu internetowego. Składa się z trzech elementów:
- protokołu,
- hosta,
- portu.
Same Origin Policy dba natomiast o to, by dwa źródła o różnych originach nie mogły wykorzystywać wzajemnie swoich elementów. Mowa tu o wszelkich działaniach, np. odpytywaniu, wywoływaniu, osadzaniu, ściąganiu itd.
Zapewne już w tym momencie widzisz pewien problem wynikający z używania mechanizmu SOP. Skoro nie pozwala on na żadną wymianę, to jak np. zapisywać i odczytywać pliki cookies ze strony A w witrynie B? W jaki sposób w originie1 umieścić arkusze CSS czy skrypty z originu2? W takiej sytuacji przecież przestałyby działać m.in. wszystkie usługi CDN. To zdecydowanie ważne wyjątki i rzeczywiście trzeba wziąć je pod uwagę.
SOP pełni więc bardzo ważną funkcję, ale jednocześnie zdarzają się sytuacje, kiedy nie powinien mieć zastosowania. Właśnie w takich sytuacjach nieoceniony okazuje się mechanizm CORS. Z jego pomocą możliwe jest wprowadzenie bezpiecznego wczytywania zapytań HTTP Cross-Origin. Serwer decyduje więc o tym, o czym pisaliśmy wyżej – czy strona „pytająca” jest godna zaufania. Jeśli tak, to pozwala na udostępnienie danych mimo tego, że origin jest inny. W ten sposób witryny internetowe mogą działać tak, jak tego chcemy, zachowując przy tym zabezpieczenia SOP.
CORS – przykład
Wyżej wspominaliśmy o sytuacjach, w których stałe, bezwarunkowe, działanie SOP mogłoby być problematyczne. Rzeczywiście takie scenariusze się zdarzają i nietrudno jest je sobie wyobrazić. Przytoczmy więc teraz – dla równowagi – czytelny przykład sytuacji, w której SORS okazuje się niezastąpiony.
Załóżmy, że masz swoją witrynę „przykladowa-strona.pl”. To rozbudowana działalność, uruchamiasz więc też do niej dwie subdomeny:
- „sklep.przykladowa-strona.pl” – do sprzedaży swoich produktów, co zasila Twój internetowy biznes,
- „platnosci.przykladowa-stron.pl” – do realizacji płatności w wygodny sposób.
W takiej sytuacji istnieje ogromne prawdopodobieństwo, że wszystkie trzy witryny należą do jednej osoby – Ciebie. W końcu opierają się na takim samym adresie i w pewien sposób są powiązane. Tak to jednak nie działa dla SOP. Mechanizm ten, w swojej podstawowej formie, blokuje komunikację między nimi. Wynika to z faktu, że są to trzy adresy różniące się originami. Pozbawia Cię to więc wygodnych integracji, utrudnia płatności klientom i niesie ze sobą szereg innych, niekorzystnych sytuacji.
Wprowadzenie CORS w takiej sytuacji sprawi, że zapytania HTTP Cross-Origin będą realizowane bezpiecznie. Strona serwująca dane będzie mogła podjąć decyzję, że zaufa subdomenom. Te z kolei, po otrzymaniu potwierdzenia, będą przesyłać klientowi odpowiedź, że dane mogą zostać mu udostępnione. Ostatecznie zatem subdomeny będą w pełni współpracować ze stroną główną.
Na czym polega CORS i jak wpływa na bezpieczeństwo?
Wiesz już, jak w teorii działa CORS i jakie jest jego główne zadanie. Czas więc przyjrzeć mu się nieco bardziej szczegółowo. Najważniejszy jest fakt, że ten mechanizm zaczyna działać dokładnie w momencie, gdy przeglądarka chce załadować zasób z innej domeny. Co się dzieje, gdy wysyła ona bezpośrednie żądanie do serwera, na którym znajdują się pożądane informacje?
Właśnie wtedy do działania rusza CORS. To on sprawia, że serwer weryfikuje, czy konkretna domena internetowa lub adres IP ma pozwolenie na pobranie żądanego zasobu. Realizowane jest to za pomocą nagłówka HTTP Access-Control-Allow-Origin. Wspomniany wcześniej adres musi więc znajdować się na liście zaakceptowanych. Tylko w takim przypadku prośba spotka się z pozytywną odpowiedzią. Dopiero wtedy też przeglądarka może pobrać dany zasób i umożliwić wykorzystanie go w witrynie internetowej.
Zadanie to realizowane jest przy zachowaniu odpowiednich zasad bezpieczeństwa. Podstawową kwestią jest fakt, że pobierane z innych domen informacje nie mają dostępu do żadnych prywatnych danych. Szczególnie istotne jest to w odniesieniu do np. danych logowania czy ciasteczek. Możliwe jest ponadto wprowadzenie dodatkowej ochrony. W takiej sytuacji serwer może zezwalać tylko na konkretne, z góry określone metody HTTP (jak np. POST i GET). Dzięki temu zminimalizowane również zostanie ryzyko ataków sieciowych.
Zapytania HTTP – proste i złożone
Można wyróżnić dwa rodzaje żądań CORS:
- żądania proste,
- żądania złożone.
Typy te sprawiają, że realizacja danego zapytania przebiega w nieco inny sposób.
Zapytanie proste musi spełniać określone kryteria. Może np. zostać określone w nim, że realizowana metoda HTTP to GET, ale nie POST. W praktyce zatem przeglądarka nie może wysyłać żadnych dodatkowych nagłówków. Tego typu zapytanie możemy wykonać też w inny sposób, np. przy użyciu XHR. Sprawdzi się w tym przypadku samowysyłający się formularz lub zapytanie GET w tagu <img>. Nie musisz więc budować żadnych dodatkowych zabezpieczeń. Ostatecznie bowiem przeglądarka i tak nie będzie mogła odczytać i wyświetlić odpowiedzi serwera klientowi.
Zapytanie złożone natomiast składa się z dodatkowych nagłówków. Umożliwiają one nawiązanie interakcji z odpowiedzią serwera. Często dotyczy to m.in. nagłówka Authorization albo Content-Type. W ten sposób np. przeglądarka weryfikuje najpierw żądaniem przedwstępnym, czy serwer przyjmie jej zapytanie. Co więcej, mogą one też wykonywać inne działania, np. ładować jakiś skrypt czy podmieniać zawartość witryny WWW.
W drugim przypadku wracamy do tematu polityki SOP. Jeśli bowiem wykryje ona potencjalne niebezpieczną odpowiedź z innego origina, to będzie w stanie ją zablokować. Niejednokrotnie natomiast zapytania złożone są przydatne i potrzebne. Przeglądarka w takiej sytuacji „przepytuje” serwer dwukrotnie.
Jak przebiega wykonanie zapytania prostego?
Za definiowanie żądań prostych odpowiadają wykorzystywane do nich nagłówki czy metody HTTP. Przede wszystkim mowa tu o:
- HEAD,
- GET,
- POST.
I nagłówkach np.:
- Accept,
- Content Language,
- Accept Language,
- Content Type (np. dla wartości multipart/form-data czy text/plain).
Sam przebieg realizacji zapytania prostego wyjaśnić można w sześciu podstawowych krokach.
- Wprowadzenie do przeglądarki przez użytkownika żądania (np. załadowania witryny internetowej „przykladowa-strona.pl”).
- Wysłanie prostego zapytania GET przez przeglądarkę do serwera.
- Zwrócenie przeglądarce w odpowiedzi dokumentu HTML. W źródle strony znajduje się JavaScript, który np. musi skomunikować się z API pod adresem „przykladowa-strona2.pl”.
- Przeglądarka weryfikuje, czy jest to zapytanie proste, czy złożone. Mamy proste zapytanie GET, więc przeglądarka zachowuje się tak, jak w przypadku zwykłego zapytania Same-Origin. Wyjątek stanowi jedynie dołączenie przez nią nagłówka origin, wskazującego na origin klienta. Stanowi on obowiązkowy element przy zapytaniach typu Cross-Origin (w Same-Origin jest on opcjonalny).
-
Zapytanie dociera do serwera. Uzupełniony nagłówek sprawia, że jest on w stanie zadecydować, czy ufa klientowi.
- Jeśli nie, to nie dodaje żadnych nagłówków i wykonuje żądanie w standardowym trybie. W takiej sytuacji wysyła odpowiedź do przeglądarki. Ta jej jednak nie wyświetli użytkownikowi ze względu na mechanizm SOP.
- Jeśli tak, to w odpowiedzi dołącza nagłówek z serii Access Control (np. Access Control Allow Origin).
- Przeglądarka otrzymuje odpowiedź serwera. Weryfikuje teraz zwrócone dane i nagłówki i postępuje zgodnie z nimi. Jeśli zezwalają na dostęp, to wyświetla odpowiedź klientowi. Jeśli takiego zezwolenia nie ma, dochodzi do zgłoszenia błędu.
Najczęściej występująca forma błędu to:
Failed to load przykladowa-strona.pl: no 'Access-Control-Allow-Origin’ header is present on the requested resource. Origin 'przykladowa-strona2.pl’ is therefore not allowed access.
Oznacza on, że zapytanie XHR zostało wykonane. Problem natomiast tkwi w fakcie, że przeglądarka zablokowała żądaną treść przed klientem. Co istotne, CORS działa „wstecz”. Jeśli serwer nie wie o jego istnieniu, to zwróci zwykłą odpowiedź.
Nagłówki w zapytaniach prostych
W zapytaniach prostych wykorzystywane są określone nagłówki:
- Origin (wymagany, ale dołączany automatycznie przez przeglądarkę),
- Access Control Allow Origin (wymagany),
- Access Control Allow Credentials (opcjonalny),
- Access Control Expose Headers (opcjonalny).
Jak przebiega wykonanie zapytania złożonego?
Przebieg zapytania złożonego jest nieco bardziej skomplikowany. Aby je szczegółowo wyjaśnić, potrzebować będziemy tym razem opisu ośmiu kroków.
- Użytkownik wystosowuje żądanie wyświetlenia witryny „przykladowa-strona.pl” w przeglądarce internetowej.
- Przeglądarka wysyła zapytanie GET to serwera.
- Serwer wysyła w odpowiedzi na żądanie przeglądarki dokument HTML. Również w tym przypadku następuje próba komunikacji z RestAPI, dostępnym pod innym originem. W tym przypadku wykorzystywany do tego jest obiekt XMLHttpRequest.
- Przeglądarka weryfikuje ponownie, z jakim typem zapytania ma styczność. Sprawdza więc w tym celu nagłówek. Jeśli nie ma go na liście dla zapytań prostych, następuje sprawdzenie, czy serwer obsługuje zapytania Cross Origin. W tym celu wykonywany jest tzw. preflight request (przeglądarka robi to automatycznie. Nie ma potrzeby dodatkowego programowania tej czynności). Zapytanie zawiera nagłówek Access-Control-Request-Method i Access-Control-request-Headers (jeśli użyte są nagłówki spoza listy zapytań prostych).
-
Serwer otrzymuje preflight. Na podstawie zawartych w nim informacji decyduje, czy ma do czynienia z klientem zaufanym:
- jeśli serwer obsługuje Cross-Origin, to zgadza się na wykonanie zapytania;
- jeśli serwer nie obsługuje Cross-Origin, żądanie nie zostanie wykonane.
-
Przeglądarka otrzymuje odpowiedź na preflight. Może wtedy sprawdzić, czy wszystkie nagłówki z serii Access-Control-*-* zostały odpowiednio ustawione:
- jeśli nagłówki są poprawne, przeglądarka wykonuje oryginalne zapytanie;
- jeśli zostanie wykryty błąd (np. nie będzie zgadzała się metoda HTTP lub origin), pojawi się błąd. Sprawi to, że nie dojdzie do wysłania docelowego zapytania.
- Serwer otrzymuje oryginalne zapytanie. Wie już, że żądanie pochodzi z zaufanego źródła i może mu zaufać. Wysyła odpowiedź.
-
Przeglądarka otrzymuje odpowiedź i weryfikuje poprawność nagłówków Access-Control-*-*:
- jeśli nagłówki są poprawne, dane są udostępniane użytkownikowi (strona zostaje wyświetlona);
- jeśli nagłówki nie są poprawne, strona nie może zostać załadowana. Zostaje zgłoszony błąd.
W tym przypadku znów dochodzi tu do tzw. kompatybilności wstecznej. Serwer nieświadomy istnienia CORS odpowie bez nagłówków Access-Control-*-* na zapytanie preflight.
Nagłówki w zapytaniach złożonych
Zapytania złożone także mają swoją listę wykorzystywanych nagłówków:
-
zapytania preflight
- origin (wymagany – dołączany automatycznie przez przeglądarkę),
- Access Control Request Method (wymagany – metoda docelowego zapytania),
- Access Control Request Headers (opcjonalny – lista z niestandardowymi nagłówkami);
-
odpowiedź preflight
- Access Control Allow Origin (wymagany – ustawiany przez serwer),
- Access Control Allow Methods (wymagany – lista metod dozwolonych przez serwer),
- Access Control Allow Headers (wymagany, gdy w zapytaniu preflight był nagłówek Access Control Request Headers),
- Access Control Allow Credentials (opcjonalny),
- Access Control Max Age (opcjonalny);
-
docelowe (oryginalne) zapytanie
- Origin (wymagany),
-
docelowa (oryginalna) odpowiedź
- Access Control Allow Origin (opcjonalny),
- Access Control Expose Headers (opcjonalny).
Jak wdrożyć CORS krok po kroku?
Wiesz już, że CORS pozwala na wykonanie asynchronicznego połączenia różnych miejsc w sieci, posiadających odmienne originy. Wymagana jest do tego natomiast zgoda danego serwisu czy aplikacji. Przy użyciu XMLHttpRequest możesz więc wykonać żądania poza swoją stroną internetową. Pozostaje więc pytanie, w jaki sposób wdrożyć CORS, by działał prawidłowo? Odpowiada za to autoryzacja CORS w nagłówku na serwerze.
Proces wdrożenia tego rozwiązania jest banalnie łatwy. Pokażemy to na przykłądzie serwera obsługującego pliki .htaccess. Wystarczy jedynie, że dodasz odpowiedni kod do swojego pliku .htaccess. Co to jest .htaccess? Jak go używać? Jest to plik konfiguracyjny Twojego serwera internetowego Apache (lub LiteSpeed). Pozwala na zmianę ustawień konfiguracji przy użyciu określonych poleceń. Możesz go edytować np.:
- z poziomu konta administratora cPanel czy DirectAdmin, do którego dostęp powinien zapewnić Ci hostingodawca,
- za pomocą programu komputerowego, by następnie wysłać go na serwer przy użyciu zewnętrznego programu (np. FileZilla, WinSCP) i połączenia z serwerem FTP, SFTP lub FTPs.
Zobacz: Jak wrzucić plik na hosting? Jak umieścić stronę na serwerze?
Po otworzeniu pliku wystarczy tylko, że dodasz do niego jedną linijkę kodu:
Header set Access-Control-Allow-Origin "*"
Znak „*” sprawia, że od tej chwili nawiązanie kontaktu nastąpić może z dowolnego originu. Możesz tam umieścić także zamiast tego inną wartość np.:
Header set Access-Control-Allow-Origin "https://przykladowa-domena.pl"
To oznacza, że zasoby na serwerze, na którym jest ten plik .htaccess, mogą być żądane tylko z domeny https://przykladowa-domena.pl. Żądania z innych domen zostaną zablokowane.
Bardzo prawdopodobne, że na Twoim serwerze znajduje się inne oznaczenie. Mowa tu o:
Header add Access-Control-Allow-Origin "localhost"
To właśnie ono sprawia, że z Twoim miejscem w sieci kontaktować się mogą wyłącznie miejsca z tym samym originem (localhost).
Pamiętaj jednak, aby przed wprowadzeniem zmian zawsze zapisać plik .htaccess. W przypadku wystąpienia problemów zawsze możesz wtedy przywrócić swoje miejsce w sieci do stabilnej formy. Po dodaniu kodu zapisz zmiany i gotowe!