Temat poruszany milion razy na forum.
Wszystko się rozbija o zabezpieczenie użytkownika przeglądarki. Wyobraź sobie, że wchodzisz na stronę hacker.com
, i ta strona robi fetcha do Twojego fejsubka albo konta w banku (np robi fetch('facebook.com/messages')
albo fetch('mbank.com/transaction-history')
. Wtedy wchodząc na losową stronę, ta strona mogłaby robić żądania do Twoich innych prywatnych stron, i one by się udały, bo jesteś na nich zalogowany w przeglądarce. Potem kod na takiej stronie może przesłać Twoje prywatne dane do takiego hackera.
Dlatego przeglądarki mają wbudowane zabezpieczenie, takie, że jeśli akurat otwarta strona (w tym wypadku ta, którą Ty napisałeś), próbuje wczytać dane z innej strony (w tym wypadku dane.biznes.gov.pl
), to ta strona myśli "a co jeśli akurat otwarta strona jest hackerem, i próbuje wczytać wrażliwe dane mojego użytkownika z dane.biznes.gov.pl
?. Dochodzi do wniosku że nie można tego wykluczyć, więc blokuje taki ruch.
Dodanie no-cors
do Twojej strony nic nie zmieni - adresowana strona nadal nie ufa Twojej witrynie (więcej o tej opcji napisał @obscurity w poście niżej).
Napisałeś:
Jeśli uruchomię ten plik js przez node otrzymuję poprawną odpowiedź
Jeśli jednak uruchamiam to przez przeglądarkę mam komunikat:
Zresztą zapytania przez curl albo poprzez wywołanie pliku js z node działa poprawnie.
No i dokładnie to jest serce Twojego problemu.
Jak strzelasz z node
, to robisz sobie po prostu request HTTP, który normalnie możesz zrobić - nikt Ci tego nie zabroni. Również uda się z curl
, z pythona czy z dowolnego innego klienta - bo odpalasz to żądanie z Twojego komputera, z Twoją sesją, z Twoimi cookiesami, z Twojej sieci z Twoim IP. Zrobisz krzywdę najwyżej sobie.
Ale w przeglądarce użytkownika sytuacja jest już inna. Jeśli zamieścisz ten kod w skrypcie w witrynie internetowej, i odpala ją użytkownik, to już nie jest Twój komputer/sesja/ip/cookies/sieć; tylko komputer i sesja Twojego użytkownika. Żądanie zostanie wysłane z jego komputera wraz z jego sesją, jego cookiesami, jego danymi, z jego sieci z jego IP. Użytkownik wtedy świadczy i ryzukuje swoimi danymi. Nie możesz go tak łatwo zmusić do tego żeby jego cookiesami i sesją strzelić do jakiegoś serwera. Kiedy użytkownik otwiera Twoją stronę, to pozwala na uruchomienie Twojego (potencjalnie szkodliwego) kodu na swojej maszynie, w swojej przeglądarce, ze swoimi cookiesami, sesją i danymi, ze swojego IP ze swojej sieci. Po prostu jesteś w stanie wtedy wykonać arbitralny kod na komputerze Twojej "ofiary" (ofiary w cudzysłowie), i np posługując się jego sesją, cookiesami i adresem, możesz np wysłać albo wczytać wrażliwe dane tego użytkownika. Mając dostęp do jego danych, i mogą robić fetch()
e z jego sesją jesteś w stanie mu wyrządzić szkodę.
Mówisz, że:
Nie chcę mi się wierzyć, że na tej stronie gdzie testowałem to API jest dodana ich domena. Zresztą zapytania przez curl albo poprzez wywołanie pliku js z node działa poprawnie.
Oprócz jawnego wpisania domeny w nagłówkach CORS, można jeszcze dodać wildcard: Access-Control-Allow-Origin: *
, i wtedy z takiej strony mogą wczytywać strony wszystkich hackerów i serverów na całym świecie. Jest to rzadko stosowane, bo jest to bardzo niebezpieczne. Wtedy użytkownik wchodząc na dowolną stronę, jest w stanie wykraść informacje z tej domeny bardzo prosto.
Działa to mniej więcej tak:
Scenariusz #1
- Użytkownik wchodzi na stronę
hacker.com
- Skrypt wczytany z tej strony próbuje wczytać Twoje wrażliwe dane, i robi np
fetch('facebook.com/messages')
- Twoja przeglądarka nie ufa skryptowi na stronie
hacker.com
, więc strzela do facebook.com
i pyta o CORS.
-
facebook.com
odsyła CORS, z której wynika że facebook.com
nie ufa hacker.com
.
- Przeglądarka odrzuca żądanie HTTP ze strony
hacker.com
do facebook.com
Scenariusz #2
- Użytkownik wchodzi na Twoją stronę
- Skrypt wczytany z tej strony próbuje wczytać dane, i robi np
fetch('https://reqbin.com/echo/get/json')
- Twoja przeglądarka strzela pod ten adres, i pyta o
Access-Control-Allow-Origin
- Strona
reqbin.com
odpowiada nagłówkiem Access-Control-Allow-Origin: *
- Przeglądarka uznaje że
reqbin.com
ufa każdej stronie, i dlatego pozwala skryptowi wczytanemu z Twojej strony na wykonanie żądania
tl;dr;
Przeglądarki mają wbudowane zabezpieczenie, które reguluje z jakiej innej witryny ktoś może się dostać do danej domeny. Czyli:
- Żądanie do strony
siema-elo.com
można wysłać z przeglądarki, tylko ze skryptu wczytanego ze strony siema-elo.com
(same-origin
)
- Żądanie do strony
siema-elo.com
można wysłać z przeglądarki, tylko ze skryptu wczytanego ze strony która jest wyróżniona w Access-Control-Allow-Origin
zwróconego z siema-elo.com
.
- Żądanie do strony
siema-elo.com
można wysłać z przeglądarki ze skryptu wczytanego z dowolnej domeny, jeśli siema-elo.com
zwróciła nagłówek Access-Control-Allow-Origin: *
Oczywiście te zabezpieczenia dotyczą skryptów z przeglądarki - bo tam operujemy sesją i cookiesami odwiedzających użytkoników. Dysponując swoją sesją (np z curl, postman, python, node'a), możemy robić requesty zawsze i wszędzie.