CORS misconfiguration
| Po angielsku | CORS misconfiguration |
|---|---|
| Którą formą pisać | po angielsku Polski odpowiednik istnieje, ale w praktyce nikt tak nie pisze. Zostaw angielski, polski trzymaj jako synonim. |
| Inne formy | błędna konfiguracja CORS · źle skonfigurowany CORS |
| Kategoria | Podatności i klasy błędów |
CORS misconfiguration to zbyt liberalna konfiguracja nagłówków CORS, która pozwala obcej stronie odczytać dane z serwisu w imieniu zalogowanego użytkownika. CORS (Cross-Origin Resource Sharing) to według PortSwigger mechanizm przeglądarki, który pozwala na kontrolowany dostęp do zasobów spoza danej domeny i rozluźnia regułę tego samego pochodzenia (same-origin policy) [2]. Źle skonfigurowany otwiera drogę do ataków między domenami [2]. MITRE opisuje pokrewną słabość jako CWE-942: politykę między domenami, która obejmuje domeny niegodne zaufania [4].
Jak działa
Przeglądarka domyślnie ogranicza żądania wysyłane przez skrypty do innych pochodzeń. MDN wyjaśnia, że fetch() i XMLHttpRequest podlegają regule tego samego pochodzenia: aplikacja może odczytać odpowiedź z innego pochodzenia tylko wtedy, gdy ta odpowiedź zawiera właściwe nagłówki CORS [1]. Główny z nich to Access-Control-Allow-Origin. Sekurak opisuje jego wartość jako pojedyncze pochodzenie albo gwiazdkę, która oznacza, że każdy może się skontaktować z serwerem za pomocą CORS [3].
Najważniejsze są żądania z danymi uwierzytelniającymi, czyli z ciasteczkami. MDN podaje dwie reguły. Jeśli serwer nie odpowie nagłówkiem Access-Control-Allow-Credentials: true, przeglądarka zignoruje odpowiedź i nie przekaże jej skryptowi [1]. Odpowiadając na takie żądanie, serwer nie może podać gwiazdki w Access-Control-Allow-Origin, tylko jawne pochodzenie [1].
Błąd powstaje, gdy programista „obchodzi” drugą regułę: zamiast gwiazdki serwer kopiuje do odpowiedzi dowolny nagłówek Origin z żądania i dodaje zgodę na ciasteczka. PortSwigger opisuje, że aplikacja odbijająca dowolne pochodzenie w Access-Control-Allow-Origin pozwala każdej domenie odczytać jej zasoby, w tym klucze API i tokeny CSRF (Cross-Site Request Forgery) [2].
Odmiany
PortSwigger wymienia kilka typowych błędów konfiguracji [2].
| Błąd | Na czym polega |
|---|---|
| odbite Origin | serwer kopiuje pochodzenie z żądania bez sprawdzenia [2] |
| błędne dopasowanie | reguła „kończy się na” przepuszcza hackersnormal-website.com [2] |
dozwolone null | przeglądarka wysyła null m.in. przy przekierowaniach i żądaniach z piaskownicy [2] |
| zaufana subdomena z XSS | skrypt wstrzyknięty w subdomenie korzysta z zaufania [2] |
| subdomena po HTTP | zaufanie do wersji bez TLS umożliwia przechwycenie [2] |
| gwiazdka | każda strona odczyta zasoby publiczne [3] |
Błędne dopasowanie często wynika z wyrażeń regularnych. Reguła „zaczyna się od” przepuszcza z kolei adres w rodzaju normal-website.com.evil-user.net [2]. Zaufanie do subdomeny łączy CORS z XSS (Cross-Site Scripting): jeśli zaufana subdomena ma lukę XSS, atakujący wykorzysta ją do odczytu danych [2]. Wariant z protokołem HTTP umożliwia atak man-in-the-middle (MITM) [2]. Na podobnej zasadzie groźne jest zaufanie do subdomen, które mogą paść ofiarą przejęcia; zob. subdomain takeover.
W WordPressie
REST API WordPressa pod adresem /wp-json/ samo wysyła nagłówki CORS. Funkcja rest_send_cors_headers(), dostępna od wersji 4.4.0, ustawia Access-Control-Allow-Origin na pochodzenie z bieżącego żądania, dodaje Access-Control-Allow-Credentials: true i nagłówek Vary: Origin [5]. Wygląda to jak opisany wyżej błąd, ale rdzeń ma drugą warstwę ochrony.
Tą warstwą jest nonce. Przy uwierzytelnianiu ciasteczkami REST API wymaga nonce z akcją wp_rest, przekazanego w parametrze _wpnonce albo nagłówku X-WP-Nonce [6]. Jeśli nonce nie ma, API ustawia bieżącego użytkownika na 0 i traktuje żądanie jak nieuwierzytelnione, nawet gdy użytkownik jest zalogowany [6]. Obca strona nie zna nonce, więc odbite pochodzenie nie daje jej dostępu do danych konta. Dokumentacja wprost wiąże ten mechanizm z ochroną przed CSRF [6].
Ryzyko pojawia się, gdy tę ochronę coś osłabi:
- wtyczka rejestruje własną trasę albo punkt końcowy
admin-ajax.php, który zwraca dane konta bez sprawdzenia nonce, i dodaje własne nagłówki CORS; to wariant niezabezpieczonego endpointu REST API; - reguła serwera, np. w pliku
.htaccessalbo konfiguracji reverse proxy, dodaje nagłówki CORS do całej witryny, łącznie z panelem; - zaufanie obejmuje subdomeny testowe, na których działa stara wersja strony.
Takie przypadki są odmianą podatności we wtyczce albo błędnej konfiguracji zabezpieczeń (security misconfiguration).
Zagrożenia i skutki
MITRE wymienia jako skutki zbyt liberalnej polityki między domenami obejście reguły tego samego pochodzenia, przeniesienie prywatnych informacji z komputera ofiary do atakującego, kradzież ciasteczek z danymi sesji i tworzenie złośliwych żądań [4]. W praktyce oznacza to ujawnienie informacji wrażliwych: danych profilu, zamówień, kluczy API. Odczytany token CSRF albo klucz API może dalej prowadzić do przejęcia konta [2].
PortSwigger zaznacza, że CORS nie chroni przed atakami między domenami, takimi jak CSRF [2]. Poprawna konfiguracja CORS ogranicza tylko odczyt odpowiedzi; nie zastępuje tokenów przeciw CSRF ani flag ciasteczek.
Jak się bronić
Zalecenia PortSwigger sprowadzają się do kilku zasad [2]:
- wpisywać do
Access-Control-Allow-Origintylko jawnie zaufane pochodzenia z listy; - nie odbijać pochodzenia z żądania bez sprawdzenia;
- nie dopuszczać wartości
null; - unikać gwiazdki w sieciach wewnętrznych;
- nie traktować CORS jako zamiennika kontroli dostępu po stronie serwera.
Gdy serwer wybiera pochodzenie dynamicznie z listy, MDN zaleca dodanie Origin do nagłówka Vary, żeby pamięci podręczne nie podawały odpowiedzi przeznaczonej dla innego pochodzenia [1]. MITRE zaleca restrykcyjną politykę i unikanie symboli wieloznacznych [4].
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Vary: Origin
CORS należy do tej samej rodziny ustawień co nagłówki bezpieczeństwa HTTP i CSP (Content Security Policy), ale działa odwrotnie: nie dodaje ograniczeń, tylko je zdejmuje. Dlatego każde odstępstwo od reguły tego samego pochodzenia powinno być wąskie i uzasadnione.
Historia i przykłady
PortSwigger podaje, że tę klasę ataków spopularyzował jego zespół badawczy prezentacją „Exploiting CORS misconfigurations for Bitcoins and bounties” [2]. Sekurak w artykule z grudnia 2018 roku omówił mechanizm po polsku i wskazał, że obejścia CORS wynikają zwykle nie z błędu samego mechanizmu, lecz z błędnej konfiguracji, nadmiernego zaufania albo niezrozumienia technologii [3]. Za podstawowy błąd uznał zbyt swobodne nadawanie praw dostępu, np. odpowiadanie gwiazdką na wszystkie żądania [3].
Przypisy
6- [1] Cross-Origin Resource Sharing (CORS) · MDN Web Docs
- [2] Cross-origin resource sharing (CORS) — Web Security Academy · PortSwigger
- [3] Czym jest CORS (Cross-Origin Resource Sharing) i jak wpływa na bezpieczeństwo · Sekurak
- [4] CWE-942: Permissive Cross-domain Security Policy with Untrusted Domains · MITRE
- [5] rest_send_cors_headers() · WordPress.org
- [6] Authentication — REST API Handbook · WordPress.org
Hasła w tym artykule
14- CSRF (Cross-Site Request Forgery)
- XSS (Cross-Site Scripting)
- man-in-the-middle (MITM)
- subdomain takeover
- nonce
- niezabezpieczony endpoint REST API
- reverse proxy
- podatność we wtyczce
- błędna konfiguracja zabezpieczeń
- ujawnienie informacji wrażliwych
- przejęcie konta
- flagi ciasteczek
- nagłówki bezpieczeństwa HTTP
- CSP (Content Security Policy)