Skutki zmiany SECRET_KEY Django

202

Popełniłem błąd i przekazałem moje projekty Django SECRET_KEYdo publicznego repozytorium.

Ten klucz powinien był być trzymany w tajemnicy zgodnie z dokumentami https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-SECRET_KEY

Projekt Django jest aktywny i działa od jakiegoś czasu z niektórymi aktywnymi użytkownikami. Jakie są skutki, jeśli zmienię SECRET_KEY? Czy wpłynie to na istniejących użytkowników, pliki cookie, sesje itp.? Oczywiście nowy SECRET_KEYnie będzie już przechowywany w miejscu publicznym.

Derek Kwok
źródło

Odpowiedzi:

199

Edycja: Ta odpowiedź jest oparta na django 1.5

SECRET_KEY jest używany w wielu różnych miejscach, najpierw wskażę, na co ma to wpływ, a następnie spróbuję przejrzeć tę listę i podać dokładne wyjaśnienie wpływu.

Lista rzeczy wykorzystujących SECRET_KEYbezpośrednio lub pośrednio:

W rzeczywistości wiele z wymienionych tutaj używać SECRET_KEYprzez django.utils.crypt.get_random_string()który wykorzystuje go do materiału siewnego silnik losowego. Zmiana wartości nie wpłynie na to SECRET_KEY.

Na wrażenia użytkowników, na które zmiana wartości ma bezpośredni wpływ, są:

  • sesjach dekodowanie danych zostanie przerwane, co jest ważne dla dowolnego zaplecza sesji (pliki cookie, baza danych, plik lub pamięć podręczna).
  • token resetowania hasła już wysłany nie będzie działać, użytkownicy będą musieli poprosić o nowy.
  • formularz komentarza (jeśli jest używany django.contrib.comments) nie zostanie zatwierdzony, jeśli został poproszony przed zmianą wartości i przesłany po zmianie wartości. Myślę, że jest to bardzo niewielkie, ale może być mylące dla użytkownika.
  • wiadomości (od django.contrib.messages) nie sprawdzą poprawności po stronie serwera w takich samych warunkach czasowych jak w przypadku formularza komentarza.

AKTUALIZACJA : teraz pracuję nad django 1.9.5, szybkie spojrzenie na źródło daje mi prawie takie same odpowiedzi. Może później dokona dokładnej inspekcji.

Sberder
źródło
1
Zmieniam SECRET_KEY na moim lokalnym serwerze deweloperskim i to mnie nie wylogowuje, więc wydaje się, że przynajmniej sesje (pamięć podręczna) działają poprawnie po zmianie. Czy mógłbyś bardziej szczegółowo wyjaśnić, co masz na myśli, data decode will breaka może wskazać kod (w django lub przykładowym projekcie), który się zepsuje? EDYCJA: nadal używasz django 1.4 - czy tak jest?
Kirill Zaitsev,
@ teferi Nie wiem o 1.4, to kwestia przyjrzenia się kodowi. Wskazałem wszystkie źródła dla każdego punktu, możesz spojrzeć na „chroń dane sesji i twórz losowe klucze sesji”. To normalne, że nadal jesteś zalogowany, ale nie będziesz w stanie odczytać danych zawartych w sesji, ponieważ SECRET_KEYjest salted_hmacużywany do mieszania danych sesji.
sberder,
jeśli jest używany do hashowania hasła, czy to nie znaczy, że hasła w bazie danych muszą zostać zresetowane?
Henning
7
@Henning Nie sądzę. Te hasła są przechowywane jako <algorithm>$<iterations>$<salt>$<hash>w AUTH_USER, więc przypadkowy sól jest przechowywany obok hasła w każdym przypadku.
Denis Drescher
2
Czy odpowiedź byłaby znacząco inna dla wersji Django> 1.5? (np. obecnie obecny 1.9)
das-g
36

Od czasu zadania tego pytania dokumentacja Django zmieniła się i zawiera odpowiedź.

Tajny klucz służy do:

  • Wszystkie sesje, jeśli używasz zaplecza sesji innego niż django.contrib.sessions.backends.cachelub używasz domyślnego get_session_auth_hash().
  • Wszystkie wiadomości, jeśli używasz CookieStoragelub FallbackStorage.
  • Wszystkie PasswordResetViewtokeny.
  • Każde użycie podpisu kryptograficznego, chyba że podany zostanie inny klucz.

Jeśli obrócisz swój tajny klucz, wszystkie powyższe zostaną unieważnione. Tajne klucze nie są używane do haseł użytkowników i rotacja kluczy nie wpłynie na nich.

Nie było dla mnie jasne, jak mam obrócić tajny klucz. Znalazłem dyskusję na temat tego, jak Django generuje klucz do nowego projektu , a także Gist, która omawia inne opcje . W końcu postanowiłem po prostu poprosić Django o utworzenie nowego projektu, skopiowanie nowego tajnego klucza do mojego starego projektu, a następnie usunięcie nowego projektu .

cd ~/junk # Go to some safe directory to create a new project.
django-admin startproject django_scratch
grep SECRET_KEY django_scratch/django_scratch/settings.py # copy to old project
rm -R django_scratch

Aktualizacja

Wygląda na to, że Django dodał get_random_secret_key()funkcję w wersji 1.10. Możesz użyć tego do wygenerowania nowego tajnego klucza.

$ ./manage.py shell -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"
s!)5@5s79sp=92a+!f4v!1g0d0+64ln3d$xm1f_7=749ht&-zi
$ ./manage.py shell -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"
_)+%kymd=f^8o_fea1*yro7atz3w+5(t2/lm2cz70*e$2mn\g3
$
Don Kirkby
źródło
4
Czy generowanie tajnego klucza opiera się na tajnym kluczu?
kdazzle,
4
Nie, @kdazzle, jeśli spojrzysz na kod źródłowystartproject , możesz zobaczyć, że generuje on losowy ciąg znaków za pomocą cryptomodułu.
Don Kirkby,
12
Hej, przepraszam, @DonKirkby, zły żart
kdazzle,
16

Według tej strony https://docs.djangoproject.com/en/dev/topics/signing/ , klucz SECRET_KEY jest używany głównie do przejściowych rzeczy - do podpisywania danych przesyłanych przewodowo, dzięki czemu można na przykład wykryć sabotaż. Wygląda na to, że MOGĄ się złamać:

  • Podpisane pliki cookie, np. „Zapamiętaj moje uwierzytelnianie na tym komputerze”, wpisz wartości. W takim przypadku plik cookie zostanie unieważniony, podpis nie zostanie zweryfikowany, a użytkownik będzie musiał ponownie uwierzytelnić.
  • W przypadku użytkowników, którzy zażądali linków do zresetowania hasła lub pobrania niestandardowego pliku, linki te nie będą już ważne. Użytkownicy musieliby po prostu ponownie poprosić o te linki.

Ktoś z bardziej aktualnym i / lub istotnym doświadczeniem Django ode mnie może zagrać inaczej, ale podejrzewam, że jeśli nie robisz czegoś wyraźnie z interfejsem API do podpisywania, powinno to powodować jedynie niewielką niedogodność dla użytkowników.

Tim Keating
źródło
6
Dlaczego więc nie wygenerować nowego klucza przy każdym ponownym uruchomieniu serwera?
osa
4
Prawdopodobnie spowodowałoby to problem, jeśli używasz tego samego serwera przy użyciu wielu procesów.
dbn
1
@osa czy chcesz wylogowywać WSZYSTKICH użytkowników za każdym razem, gdy wypychasz kod / restartujesz serwer?
EralpB
6

Ciąg SECRET_KEY służy przede wszystkim do szyfrowania i / lub mieszania danych cookie. Wiele frameworków (w tym Django) przychodzi do tego, ponieważ domyślne pliki cookie sesji mają swoje wady.

Wyobraź sobie, że masz formularz w django do edytowania artykułów z ukrytym polem. W tym ukrytym polu przechowywany jest identyfikator edytowanego artykułu. A jeśli chcesz mieć pewność, że nikt nie wyśle ​​Ci żadnego innego identyfikatora artykułu, dodasz dodatkowe ukryte pole z hashowanym identyfikatorem. Więc jeśli ktoś zmieni identyfikator, będziesz o tym wiedział, ponieważ skrót nie będzie taki sam.

Oczywiście jest to trywialny przykład, ale w ten sposób używany jest SECRET_KEY.

Django wewnętrznie używa go na przykład do {% csrf_token%} i kilku innych rzeczy. To naprawdę nie powinno mieć żadnego wpływu na twoją aplikację, jeśli ją zmienisz, w oparciu o twoje pytanie i że nie używasz jej.

Jedyną rzeczą jest to, że może wartości sesji zostaną usunięte. Na przykład użytkownicy będą musieli ponownie zalogować się do administratora, ponieważ django nie będzie w stanie dekodować sesji przy użyciu innego klucza.

ciemne
źródło
1

Popełniłem ten sam błąd. Domyślne hasło miało długość 50, więc użyłem programu PowerShell do wygenerowania losowego ciągu o długości 50 i zastąpiłem go starym kluczem SECRET_KEY. Byłem zalogowany i po wymianie klucza SECRET_KEY moja poprzednia sesja została unieważniona.

Z Powershell ( źródło ):

# Load the .net System.Web namespace which has the GeneratePassword function
[Reflection.Assembly]::LoadWithPartialName("System.Web")

#  GeneratePassword(int length, int numberOfNonAlphanumericCharacters)
[System.Web.Security.Membership]::GeneratePassword(50,5)

Z Bash ( źródło ):

# tr includes ABCabc123 and the characters from OWASP's "Password special characters list"
cat /dev/urandom | tr -dc 'A-Za-z0-9!"#$%&\''()*+,-./:;<=>?@[\]^_`{|}~' | head -c 100 ; echo

W tym momencie pomyślałem, dlaczego nie spróbować większego klucza, więc wypróbowałem go z kluczem o długości 100 i 1000. Oba działały. Jeśli rozumiem kod źródłowy , obiekt zwrócony przez funkcję osoby podpisującej jest skrótem hmac w base64. RFC 2104 ma to do powiedzenia na temat wymaganej długości tajnego klucza HMAC.

Aplikacje, które używają kluczy dłuższych niż B, najpierw haszują klucz za pomocą H, a następnie używają wynikowego łańcucha L bajtów jako rzeczywistego klucza do HMAC.

Klucz dla HMAC może mieć dowolną długość (klucze dłuższe niż B bajty są najpierw haszowane przy użyciu H). Jednak zdecydowanie mniej niż L bajtów jest odradzane, ponieważ zmniejszyłoby to siłę bezpieczeństwa funkcji. Klucze dłuższe niż L bajtów są dopuszczalne, ale dodatkowa długość nie zwiększyłaby znacząco siły funkcji. (Dłuższy klucz może być wskazany, jeśli losowość klucza jest uważana za słabą).

Aby przetłumaczyć na zwykłe mówienie, rozmiar tajnego klucza musi być tego samego rozmiaru co wynik. Klucz musi być również w bitach. Każda cyfra w base64 reprezentuje 6 bitów. Więc jeśli miałbyś 50-znakowe hasło, miałbyś tajny klucz 50 x 6 = 300 bitów. Jeśli używasz SHA256, potrzebujesz 256-bitowego klucza ( sha256 z definicji używa 256 bitów ). Dlatego 50 długie hasło powinno działać, chyba że planujesz użyć algorytmu skrótu większego niż SHA256.

Ale ponieważ wszelkie dodatkowe bity klucza są haszowane, jego rozmiar nie drastycznie obniży wydajność. Ale to zagwarantuje ci, że masz wystarczającą ilość bitów do większych funkcji skrótu. SHA-512 byłby objęty 100 długim kluczem SECRET_KEY ( 50 x 6 = 600 bitów> 512 bitów ).

Rex Linder
źródło