Poniższa odpowiedź dotyczy przede wszystkim Signed Cookies , czyli implementacji koncepcji sesji (używanej w aplikacjach internetowych). Flask oferuje zarówno zwykłe (niepodpisane) pliki cookie (przez request.cookies
i response.set_cookie()
), jak i podpisane (przez flask.session
). Odpowiedź składa się z dwóch części: pierwsza opisuje sposób generowania podpisanego pliku cookie, a druga jest przedstawiona w formie kontroli jakości, która dotyczy różnych aspektów schematu. Składnia użyta w przykładach to Python3, ale koncepcje odnoszą się również do poprzednich wersji.
Co to jest SECRET_KEY
(lub jak utworzyć podpisany plik cookie)?
Podpisywanie plików cookie jest środkiem zapobiegawczym przeciwko manipulowaniu plikami cookie. Podczas procesu podpisywania pliku cookie SECRET_KEY
jest on używany w sposób podobny do tego, jak „sól” byłaby używana do pomieszania hasła przed jego zahaszowaniem. Oto (szalenie) uproszczony opis pojęcia. Kod w przykładach ma charakter ilustracyjny. Wiele kroków zostało pominiętych i nie wszystkie funkcje faktycznie istnieją. Celem jest zapewnienie zrozumienia ogólnej idei, rzeczywiste wdrożenia będą nieco bardziej zaangażowane. Pamiętaj też, że Flask robi większość tego za Ciebie w tle. Tak więc, oprócz ustawiania wartości dla pliku cookie (za pośrednictwem interfejsu API sesji) i dostarczania pliku SECRET_KEY
, nie tylko niezradzane jest ponowne wdrażanie tego samodzielnie, ale nie ma takiej potrzeby:
Podpis pliku cookie biedaka
Przed wysłaniem odpowiedzi do przeglądarki:
(1) Najpierw powstaje a SECRET_KEY
. Powinien być znany tylko aplikacji i powinien być utrzymywany na względnie stałym poziomie podczas cyklu życia aplikacji, w tym podczas ponownego uruchamiania aplikacji.
# choose a salt, a secret string of bytes
>>> SECRET_KEY = 'my super secret key'.encode('utf8')
(2) utwórz plik cookie
>>> cookie = make_cookie(
... name='_profile',
... content='uid=382|membership=regular',
... ...
... expires='July 1 2030...'
... )
>>> print(cookie)
name: _profile
content: uid=382|membership=regular...
...
...
expires: July 1 2030, 1:20:40 AM UTC
(3) aby utworzyć podpis, dołącz (lub dodaj przed nim) SECRET_KEY
ciąg bajtów pliku cookie, a następnie wygeneruj skrót z tej kombinacji.
# encode and salt the cookie, then hash the result
>>> cookie_bytes = str(cookie).encode('utf8')
>>> signature = sha1(cookie_bytes+SECRET_KEY).hexdigest()
>>> print(signature)
7ae0e9e033b5fa53aa....
(4) Teraz umieść podpis na jednym końcu content
pola oryginalnego pliku cookie.
# include signature as part of the cookie
>>> cookie.content = cookie.content + '|' + signature
>>> print(cookie)
name: _profile
content: uid=382|membership=regular|7ae0e9... <--- signature
domain: .example.com
path: /
send for: Encrypted connections only
expires: July 1 2030, 1:20:40 AM UTC
i to właśnie jest wysyłane do klienta.
# add cookie to response
>>> response.set_cookie(cookie)
# send to browser -->
Po otrzymaniu pliku cookie z przeglądarki:
(5) Kiedy przeglądarka zwraca ten plik cookie z powrotem na serwer, usuń podpis z pola pliku cookie, content
aby odzyskać oryginalny plik cookie.
# Upon receiving the cookie from browser
>>> cookie = request.get_cookie()
# pop the signature out of the cookie
>>> (cookie.content, popped_signature) = cookie.content.rsplit('|', 1)
(6) Użyj oryginalnego pliku cookie z aplikacją, SECRET_KEY
aby przeliczyć podpis, używając tej samej metody, co w kroku 3.
# recalculate signature using SECRET_KEY and original cookie
>>> cookie_bytes = str(cookie).encode('utf8')
>>> calculated_signature = sha1(cookie_bytes+SECRET_KEY).hexdigest()
(7) Porównaj obliczony wynik z podpisem wyskakującym z właśnie otrzymanego pliku cookie. Jeśli pasują, wiemy, że plik cookie nie został zmieniony. Ale jeśli nawet spacja została dodana do pliku cookie, podpisy nie będą pasować.
# if both signatures match, your cookie has not been modified
>>> good_cookie = popped_signature==calculated_signature
(8) Jeśli się nie zgadzają, możesz odpowiedzieć dowolną liczbą działań, zarejestrować zdarzenie, odrzucić plik cookie, wydać nowy, przekierować na stronę logowania itp.
>>> if not good_cookie:
... security_log(cookie)
Kod uwierzytelniania wiadomości oparty na skrótach (HMAC)
Typ podpisu wygenerowanego powyżej, który wymaga tajnego klucza, aby zapewnić integralność niektórych treści, jest nazywany w kryptografii kodem uwierzytelniania wiadomości lub adresem MAC .
Sprecyzowałem wcześniej, że powyższy przykład jest nadmiernym uproszczeniem tej koncepcji i że nie jest dobrym pomysłem wdrażanie własnego podpisu. Dzieje się tak, ponieważ algorytm używany do podpisywania plików cookie w Flasku nazywa się HMAC i jest nieco bardziej zaangażowany niż powyższy prosty krok po kroku. Ogólna idea jest taka sama, ale z powodów wykraczających poza zakres tej dyskusji seria obliczeń jest odrobinę bardziej złożona. Jeśli nadal jesteś zainteresowany stworzeniem majsterkowania, jak to zwykle bywa, Python ma kilka modułów, które pomogą Ci zacząć :) oto blok startowy:
import hmac
import hashlib
def create_signature(secret_key, msg, digestmod=None):
if digestmod is None:
digestmod = hashlib.sha1
mac = hmac.new(secret_key, msg=msg, digestmod=digestmod)
return mac.digest()
Dokumentacja dla hmac i hashlib .
„Demistyfikacja” SECRET_KEY
:)
Co to jest „podpis” w tym kontekście?
Jest to metoda zapewniająca, że niektóre treści nie zostały zmodyfikowane przez nikogo poza osobą lub podmiotem do tego upoważnionym.
Jedną z najprostszych form podpisu jest „ suma kontrolna ”, która po prostu sprawdza, czy dwie części danych są takie same. Na przykład podczas instalowania oprogramowania ze źródła ważne jest, aby najpierw potwierdzić, że Twoja kopia kodu źródłowego jest identyczna z kopią jego autora. Typowym podejściem do tego jest uruchomienie źródła za pomocą kryptograficznej funkcji skrótu i porównanie wyników z sumą kontrolną opublikowaną na stronie głównej projektu.
Załóżmy na przykład, że masz zamiar pobrać źródło projektu w pliku gzip z serwera lustrzanego. Suma kontrolna SHA1 opublikowana na stronie internetowej projektu to „eb84e8da7ca23e9f83 ....”
# so you get the code from the mirror
download https://mirror.example-codedump.com/source_code.tar.gz
# you calculate the hash as instructed
sha1(source_code.tar.gz)
> eb84e8da7c....
Oba skróty są takie same, wiesz, że masz identyczną kopię.
Co to jest plik cookie?
Obszerna dyskusja na temat plików cookie wykraczałaby poza zakres tego pytania. Przedstawiam tutaj przegląd, ponieważ minimalne zrozumienie może być przydatne, aby lepiej zrozumieć, jak i dlaczego SECRET_KEY
jest przydatne. Gorąco zachęcam do dalszych osobistych lektur na temat plików cookie HTTP.
Powszechną praktyką w aplikacjach internetowych jest używanie klienta (przeglądarki internetowej) jako niewielkiej pamięci podręcznej. Pliki cookie są jedną z realizacji tej praktyki. Plik cookie to zazwyczaj pewne dane dodawane przez serwer do odpowiedzi HTTP za pośrednictwem jego nagłówków. Jest przechowywany przez przeglądarkę, która następnie odsyła go z powrotem do serwera podczas wysyłania żądań, również za pośrednictwem nagłówków HTTP. Dane zawarte w pliku cookie mogą służyć do emulacji tzw Stanu, iluzja, że serwer utrzymuje ciągłe połączenie z klientem. Tylko w tym przypadku zamiast okablowania utrzymującego połączenie „przy życiu” masz po prostu migawki stanu aplikacji po tym, jak obsłużyła ona żądanie klienta. Te migawki są przenoszone między klientem a serwerem. Po otrzymaniu żądania serwer najpierw odczytuje zawartość pliku cookie, aby przywrócić kontekst swojej rozmowy z klientem. Następnie obsługuje żądanie w tym kontekście i przed zwróceniem odpowiedzi klientowi aktualizuje plik cookie. W ten sposób zostaje zachowana iluzja trwającej sesji.
Jak wygląda plik cookie?
Typowy plik cookie wyglądałby tak:
name: _profile
content: uid=382|status=genie
domain: .example.com
path: /
send for: Encrypted connections only
expires: July 1 2030, 1:20:40 AM UTC
Pliki cookie są łatwe do odczytania w każdej nowoczesnej przeglądarce. Na przykład w przeglądarce Firefox przejdź do Preferencje> Prywatność> Historia> usuń poszczególne pliki cookie .
content
Pole jest najbardziej odpowiednie dla danej aplikacji. Inne pola zawierają głównie instrukcje meta, aby określić różne zakresy wpływu.
Po co w ogóle używać plików cookie?
Krótka odpowiedź to wydajność. Korzystanie z plików cookie minimalizuje potrzebę wyszukiwania rzeczy w różnych magazynach danych (pamięci podręczne, pliki, bazy danych itp.), Przyspieszając w ten sposób działanie po stronie aplikacji serwera. Należy pamiętać, że im większy plik cookie, tym większy ładunek w sieci, więc to, co zapisujesz podczas wyszukiwania bazy danych na serwerze, możesz stracić w sieci. Zastanów się dokładnie, co umieścić w swoich plikach cookie.
Dlaczego pliki cookie miałyby być podpisywane?
Pliki cookie są używane do przechowywania wszelkiego rodzaju informacji, z których niektóre mogą być bardzo wrażliwe. Z natury nie są one również bezpieczne i wymagają podjęcia szeregu dodatkowych środków ostrożności, aby w jakikolwiek sposób zostały uznane za bezpieczne dla obu stron, klienta i serwera. Podpisywanie plików cookie w szczególności rozwiązuje problem, przy którym można majstrować podczas prób oszukania aplikacji serwerowych. Istnieją inne sposoby ograniczania innych rodzajów luk, zachęcam do przeczytania więcej o plikach cookie.
Jak można majstrować przy ciasteczku?
Pliki cookie znajdują się na kliencie w formie tekstowej i można je edytować bez wysiłku. Plik cookie otrzymany przez Twoją aplikację serwera mógł zostać zmodyfikowany z wielu powodów, z których niektóre mogą nie być niewinne. Wyobraź sobie aplikację internetową, która przechowuje informacje o uprawnieniach swoich użytkowników w plikach cookie i przyznaje uprawnienia na podstawie tych informacji. Jeśli plik cookie nie jest odporny na majsterkowanie, każdy może zmodyfikować swój plik, aby podnieść jego status z „role = visitor” do „role = admin”, a aplikacja nie byłaby mądrzejsza.
Dlaczego SECRET_KEY
podpisywanie plików cookie jest konieczne?
Weryfikacja plików cookie różni się nieco od weryfikacji kodu źródłowego w sposób opisany wcześniej. W przypadku kodu źródłowego oryginalnym autorem jest powiernik i właściciel referencyjnego odcisku palca (sumy kontrolnej), który będzie publicznie dostępny. Nie ufasz kodowi źródłowemu, ale ufasz publicznemu podpisowi. Aby zweryfikować swoją kopię źródła, po prostu chcesz, aby obliczony hash był zgodny z publicznym hashem.
Jednak w przypadku pliku cookie aplikacja nie śledzi podpisu, tylko go śledzi SECRET_KEY
. To SECRET_KEY
jest referencyjny odcisk palca. Pliki cookie są przesyłane z podpisem, który twierdzi, że jest legalny. Legalność oznacza tutaj, że podpis został wystawiony przez właściciela ciasteczka, czyli aplikację, aw tym przypadku jest to oświadczenie, że nie ufasz i musisz sprawdzić podpis pod kątem ważności. Aby to zrobić, musisz dołączyć do podpisu element, który jest znany tylko Tobie, to jest plik SECRET_KEY
. Ktoś może zmienić plik cookie, ale ponieważ nie ma tajnego składnika do prawidłowego obliczenia prawidłowego podpisu, nie może go sfałszować. Jak wspomniano nieco wcześniej, ten rodzaj odcisków palców, w którym na górze sumy kontrolnej znajduje się również tajny klucz,
A co z sesjami?
Sesje w ich klasycznej implementacji to pliki cookie, które zawierają tylko identyfikator w content
terenie, plik session_id
. Cel sesji jest dokładnie taki sam, jak podpisane pliki cookie, tj. Zapobieganie manipulowaniu plikami cookie. Sesje klasyczne mają jednak inne podejście. Po otrzymaniu pliku cookie sesji serwer używa identyfikatora do wyszukiwania danych sesji we własnej pamięci lokalnej, którą może być baza danych, plik lub czasami pamięć podręczna w pamięci. Plik cookie sesji zazwyczaj wygasa po zamknięciu przeglądarki. Ze względu na krok wyszukiwania w pamięci lokalnej ta implementacja sesji zazwyczaj powoduje spadek wydajności. Podpisane pliki cookie stają się preferowaną alternatywą i w ten sposób implementowane są sesje Flask. Innymi słowy, sesje Flask topodpisane pliki cookie, a aby używać podpisanych plików cookie w usłudze Flask, wystarczy użyć jego Session
interfejsu API.
Dlaczego nie zaszyfrować plików cookie?
Czasami zawartość plików cookie może być zaszyfrowana przed podpisaniem . Dzieje się tak, jeśli zostaną uznane za zbyt wrażliwe, aby były widoczne w przeglądarce (szyfrowanie ukrywa zawartość). Samo podpisanie plików cookie odpowiada jednak innym potrzebom, takim, w którym istnieje chęć zachowania pewnego stopnia widoczności i użyteczności plików cookie w przeglądarce, jednocześnie zapobiegając ingerencji w nie.
Co się stanie, jeśli zmienię SECRET_KEY
?
Zmieniając ustawienia SECRET_KEY
, unieważniasz wszystkie pliki cookie podpisane poprzednim kluczem. Gdy aplikacja otrzyma żądanie z ciasteczkiem podpisanym poprzednim SECRET_KEY
, spróbuje obliczyć podpis nowym SECRET_KEY
, a oba podpisy nie będą pasować, ten plik cookie i wszystkie jego dane zostaną odrzucone, będzie tak, jakby przeglądarka łączy się z serwerem po raz pierwszy. Użytkownicy zostaną wylogowani, a ich stare pliki cookie zostaną zapomniane, a także wszystko, co jest w nich przechowywane. Zwróć uwagę, że różni się to od sposobu obsługi wygasłego pliku cookie. Dzierżawa wygasłego pliku cookie może zostać przedłużona, jeśli jego podpis zostanie sprawdzony. Nieprawidłowy podpis oznacza po prostu zwykły nieprawidłowy plik cookie.
Jeśli więc nie chcesz unieważnić wszystkich podpisanych plików cookie, spróbuj zachować SECRET_KEY
to samo przez dłuższy czas.
Co jest dobre SECRET_KEY
?
Tajny klucz powinien być trudny do odgadnięcia. Dokumentacja dotycząca sesji zawiera dobry przepis na losowe generowanie kluczy:
>>> import os
>>> os.urandom(24)
'\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O<!\xd5\xa2\xa0\x9fR"\xa1\xa8'
Kopiujesz klucz i wklejasz go do pliku konfiguracyjnego jako wartość SECRET_KEY
.
Oprócz użycia losowo wygenerowanego klucza, możesz użyć złożonego zestawu słów, liczb i symboli, być może ułożonych w zdaniu znanym tylko tobie, zakodowanym w postaci bajtów.
Czy nie ustawić SECRET_KEY
bezpośrednio z funkcją, która generuje inny klucz za każdym razem to się nazywa. Na przykład nie rób tego:
# this is not good
SECRET_KEY = random_key_generator()
Za każdym razem, gdy aplikacja zostanie uruchomiona ponownie, otrzyma nowy klucz, unieważniając w ten sposób poprzedni.
Zamiast tego otwórz interaktywną powłokę Pythona i wywołaj funkcję w celu wygenerowania klucza, a następnie skopiuj i wklej go do pliku config.