Spring Security: CORS (Cross-Origin Resource Sharing)


Po co nam ten CORS?

Obecnie bardzo rzadko piszemy aplikacje, które nie wysyłają zapytań do innych źródeł. Typowy jest już chociażby podział na Frontend i Backend, które znajdują się na różnych domenach.

Wyobraź sobie taką sytuację. Tworzysz portal z ogłoszeniami. Zaczynasz od frontendu w React. Niech będzie to strona z ostatnimi ogłoszeniami. 'npm start’ i wszystko hula. Teraz backend. Wystawiasz endpointy. Backend też wstaje, nie ma błędów, super. Podkładasz pod statyczne, tymczasowe dane na frontendzie zapytania do backendu. No to co, wystarczy tylko uruchomić obie aplikacje.

Coś nie działa. Sprawdzamy konsolę. Naszym oczom ukazuje się taki obrazek:

cors-error

Pierwsza myśl: to wszystko przez ten CORS. A tak naprawdę to wszystko przez jego brak. Wiele osób błędnie myśli, że CORS nie pozwala nam na komunikację między serwisami postawionymi na różnych domenach, ale jest wręcz przeciwnie. CORS pozwala nam poluźnić restrykcje i w kontrolowany sposób dopuścić zapytania do naszego backendu tylko z serwisów, którym chcemy na to pozwolić.

Jak to działa?

Mechanizm ten opiera się na nagłówkach odpowiedzi:

Access-Control-Allow-Origin – określa domeny (origins), którym pozwalamy na dostęp do odpowiedzi z naszego backendu
Access-Control-Allow-Methods – pozwala określić konkretne metody, którymi będą komunikować się wyżej określone obce serwisy (np. GET, POST)
Access-Control-Allow-Headers – określa dozwolone nagłówki w zapytaniach z obcych serwisów

Uwaga: Wyżej opisane nagłówki dotyczą odpowiedzi z backendu, nie zapytań! Jest to bardzo ważne, ponieważ zapytania nie spełniające naszych wymagań i tak mogą być wysyłane do backendu. Różnica polega tylko na tym, że nasza przeglądarka zablokuje przekazanie odpowiedzi z backendu do frontendu. Przeglądarka wysyła tzw. pre-flight request z nagłówkiem zawierającym origin (domenę) strony. Backend wysyła odpowiedź z wyżej opisanymi nagłówkami. Następnie przeglądarka sprawdza czy jej zapytanie spełnia określone w nagłówkach odpowiedzi warunki. Jeśli tak odpowiedź jest przyjęta przez nasz frontend.

Innymi słowy: Zapytanie z frontendu trafia na backend z nagłówkiem zawierającym origin, dajmy na to „https://myprettyfrontend.com”. Odpowiedź backendu opatrywana jest nagłówkiem np. Access-Control-Allow-Origin: „https://myprettyfrontend.com”. Przeglądarka rozpoznaje origin z nagłówka odpowiedzi, jako swój. Odpowiedź trafia na frontend. Wszyscy są szczęśliwi.

Implementacja CORS w Spring

Możemy przejść do Spring Security. W Springu mamy dwa sposoby na konfigurację CORS. Dodawanie adnotacji do kontrolerów lub dodanie odpowiedniego filtra do klasy konfiguracji bezpieczeństwa (np. SecurityConfig.class).

Zacznijmy od prostszej metody, tj. dodania adnotacji. Adnotację @CrossOrigin możemy dodać do całej klasy lub do konkretnej metody w kontrolerze:

cors-crossorigin-adnotation

Jak widać w drugim przykładzie, możemy również podać listę dozwolonych domen. Dodatkowo możemy określić pozostałe opisane wcześniej parametry. W poniższym przykładzie pozwalamy na wszystkie nagłówki:

Możemy również zezwolić na zapytania ze wszystkich domen wstawiając '*’ zamiast domeny, ale jest to bardzo zła praktyka, a już na pewno zakazana w środowisku produkcyjnym. Naraża to naszą aplikację na ataki DDoS i XSS.

Metoda z adnotacją pozwala na wygodne i selektywne dodawanie zasad CORS do poszczególnych endpointów, ale może również prowadzić do zmniejszenia czytelności kodu lub przeoczeń.

Druga metoda

Druga metoda to dodanie odpowiednich filtrów w klasie z konfiguracją bezpieczeństwa. Dodajemy cors() do naszych filtrów. Cors() jako parametr przyjmuje obiekt Customizer<CorsConfigurer>, dla którego określamy CorsConfigurationSource. A to wszystko poniżej:

cors-securityfilterchain

Uwagi: W powyższym przykładzie url-e zaciągane są z pliku konfiguracyjnego .yml. Możesz wpisać domeny bezpośrednio (np. „http://localhost:3000”). Jeżeli chodzi o csrf() to jest on wyłączony dla celów testowych. Nie jest rozwiązanie dopuszczalne w produkcyjnych wersjach aplikacji.

W CorsConfigurationSource, w przeciwieństwie do poprzedniego sposobu, oprócz origins koniecznie musimy podać metody. Inaczej nasz filtr nie zadziała.

Jeśli po zaimplementowaniu CORS wciąż widzisz błędy z tym powiązane, sprawdź konfigurację CSRF.

Więcej na temat CORS: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

Jeśli dowiedziałeś się czegoś z tego wpisu lub pomogłem rozwiązać twój problem, zostaw proszę komentarz. Jeśli rzuciły ci się w oczy jakieś błędy, literówki czy moje błędne rozumowanie, również podziel się tym w komentarzu – wprowadzę poprawki 😉

,

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *