10.12. CORS i CSRF

CORS i CSRF to dwa zabezpieczenia po stronie przeglądarki, których kamera wystawiona na otwarty internet potrzebuje obok HTTPS i logowania. Każde wymaga kilku linii konfiguracji. Poniższe sekcje definiują pojęcie i pokazują integrację z microdotem.

10.12.1. Do czego służy CORS

Cross-Origin Resource Sharing (CORS) to mechanizm przeglądarki pozwalający serwerowi świadomie zezwolić wybranym innym źródłom na odczyt jego odpowiedzi. Domyślna polityka tego samego źródła przeglądarki blokuje ten odczyt: JavaScript na https://example.com nie może odczytać odpowiedzi z https://yard-cam.example.com, ponieważ inny host liczy się jako inne źródło. CORS to sposób, w jaki serwer udziela wyjątków wybranym partnerom.

Jeśli panel jest serwowany z samej kamery, każde żądanie pochodzi z tego samego źródła i CORS nic nie robi. Konfiguracja ma znaczenie, gdy panel mieszka gdzie indziej – publiczny adres URL taki jak https://app.example.com, który komunikuje się z kamerą pod https://yard-cam.example.com:

from microdot.cors import CORS

cors = CORS(
    app,
    allowed_origins=['https://app.example.com'],
    allow_credentials=True,
    max_age=86400,
)

allowed_origins to lista źródeł, którym wolno odczytywać odpowiedzi kamery. Źródło panelu i tylko źródło panelu – nie * – aby witryna strony trzeciej nie mogła przypadkiem odczytać odpowiedzi kamery.

allow_credentials=True pozwala żądaniom z innego źródła dołączać ciasteczko sesji, czego panel potrzebuje, aby pozostać zalogowanym ponad granicą źródeł.

max_age=86400 mówi przeglądarce, że może buforować wynik kontroli wstępnej przez dobę. Przeglądarki wysyłają dodatkowe żądanie OPTIONS przed każdym wywołaniem z innego źródła, które używa metod innych niż GET/HEAD/POST lub wysyła niestandardowe nagłówki; max_age ogranicza ten narzut do jednej kontroli wstępnej dziennie na trasę.

10.12.2. Do czego służy CSRF

Cross-Site Request Forgery (CSRF) to atak, w którym złośliwa strona sprawia, że przeglądarka użytkownika wysyła uwierzytelnione żądanie do zaufanego serwera. Nawet gdy CORS jest na miejscu, ukryty <form> na evil.com, który wykonuje POST do https://yard-cam.example.com/config, dotrze do kamery, a przeglądarka dołączy ciasteczko sesji kamery – ciasteczka podążają za hostem docelowym, a nie za źródłem strony wysyłającej żądanie – więc kamera ochoczo przetworzy POST tak, jakby pochodził od właściciela.

Ochrona CSRF odrzuca takie żądania. microdot.csrf.CSRF dodaje oprogramowanie pośredniczące, które sprawdza nagłówek Sec-Fetch-Site w każdym żądaniu zmieniającym stan i odrzuca wszystko, co nie jest oznaczone jako same-origin (lub nie pochodzi ze źródła dozwolonego przez CORS):

from microdot.csrf import CSRF

CSRF(app, cors=cors)

Przekazanie instancji cors pozwala oprogramowaniu pośredniczącemu uznać dozwolone źródło panelu za już zatwierdzone – kamera nadal akceptuje żądania POST z panelu, mimo że pochodzą z innego źródła.

Sec-Fetch-Site jest ustawiany automatycznie przez nowoczesne przeglądarki; kamera nie musi robić nic po stronie klienta. Dla starszych przeglądarek, które nie wysyłają tego nagłówka, lista dozwolonych źródeł CORS jest kontrolą awaryjną.

10.12.3. Wyłączanie webhooków

Jeśli kamera potrzebuje punktu końcowego webhooka, aby przyjmować żądania POST od usługi chmurowej strony trzeciej – na przykład wywołanie zwrotne od dostawcy archiwum – oznacz trasę jako @csrf.exempt, aby oprogramowanie pośredniczące ją przepuściło. Handler jest odpowiedzialny za weryfikację żądania w inny sposób – zwykle za pomocą Hash-based Message Authentication Code (HMAC) nad ładunkiem, obliczanego z sekretem współdzielonym przez kamerę i stronę trzecią, co dowodzi, że żądanie pochodzi od kogoś, kto znał sekret. Kamera na podwórku nie ma żadnego z nich, ale dekorator jest na miejscu, gdy go potrzebujesz.

10.12.4. Czterowierszowa podstawa

Gdy HTTPS jest na miejscu, zalecany stos dla każdego wdrożenia kamery wystawionej na internet to:

Session(app, secret_key=SECRET,
        cookie_options={'http_only': True, 'secure': True})
login = Login()
cors = CORS(app, allowed_origins=[...], allow_credentials=True)
CSRF(app, cors=cors)

Sesja i logowanie wcześniej w rozdziale, CORS i CSRF tutaj, HTTPS z poprzedniego tematu. Te cztery elementy układają się jeden na drugim i nie wchodzą w drogę żadnej trasie.

Kamera może teraz bezpiecznie zmierzyć się z otwartym internetem – HTTPS, logowanie, CSRF, CORS.