Эффекты изменения SECRET_KEY в Django

202

Я сделал ошибку и поместил свой проект Django SECRET_KEYв публичный репозиторий.

Этот ключ должен был храниться в секрете в соответствии с документами https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-SECRET_KEY

Проект Django является живым и работает некоторое время с некоторыми активными пользователями. Каковы последствия, если я изменю SECRET_KEY? Будут ли затронуты какие-либо существующие пользователи, файлы cookie, сеансы и т. Д.? Очевидно, что новый SECRET_KEYбольше не будет храниться в публичном месте.

Дерек Квок
источник

Ответы:

199

Изменить: Этот ответ основан на Django 1,5

SECRET_KEY используется во многих различных местах, я сначала укажу, на что это влияет, а затем попробую просмотреть этот список и дать точное объяснение воздействия.

Список вещей, использующих SECRET_KEYпрямо или косвенно:

На самом деле очень много пунктов , перечисленных здесь , используют SECRET_KEYчерез django.utils.crypt.get_random_string()который использует его для семян случайного двигателя. На это не повлияет изменение стоимости SECRET_KEY.

Пользовательский опыт, непосредственно затронутый изменением значения:

  • сеансах, декодирование данных будет прервано, что справедливо для любого сеанса бэкэнда (куки, база данных, файл или кеш).
  • уже отправленный токен для сброса пароля не будет работать, пользователям придется запросить новый.
  • Форма комментариев (если используется django.contrib.comments) не будет проверена, если она была запрошена до изменения значения и отправлена ​​после изменения значения. Я думаю, что это очень незначительно, но может сбить пользователя с толку.
  • messages (from django.contrib.messages) не будет проверять серверную часть в тех же временных условиях, что и для формы комментариев.

ОБНОВЛЕНИЕ : теперь работает над django 1.9.5, быстрый взгляд на источник дает мне почти такие же ответы. Могу сделать тщательный осмотр позже.

sberder
источник
1
Я изменяю SECRET_KEY на моем локальном сервере разработки, и это не выводит меня из системы, поэтому кажется, что по крайней мере сессии (кэш) работают правильно после изменения. Не могли бы вы рассказать подробнее о том, что вы имеете в виду, data decode will breakи, возможно, указать какой-нибудь код (в django или в примере проекта), который сломается? РЕДАКТИРОВАТЬ: по-прежнему использовать Django 1.4 - это так?
Кирилл Зайцев
@teferi Я не знаю, что такое 1.4, это вопрос кода. Я указал на все источники для каждой точки, вы можете взглянуть на «Защита данных сеанса и создание случайных ключей сеанса». Это нормально, что вы все еще вошли в систему, но вы не сможете прочитать данные, содержащиеся в сеансе, которые SECRET_KEYиспользуются salted_hmacдля хеширования данных сеанса.
Сбердер
если он используется для хэша соли пароля, не означает ли это, что пароли в базе данных должны быть сброшены?
Хеннинг
7
@ Хеннинг, я так не думаю. Эти пароли хранятся в виде <algorithm>$<iterations>$<salt>$<hash>в AUTH_USER, поэтому случайная соль хранится вместе с паролем в каждом конкретном случае.
Денис Дрешер
2
Будет ли ответ значительно отличаться для версий Django> 1.5? (например, теперь текущий 1.9)
DAS-г
36

Поскольку этот вопрос был задан, документация по Django была изменена и теперь содержит ответ.

Секретный ключ используется для:

  • Все сеансы, если вы используете какой-либо другой сеансовый бэкэнд django.contrib.sessions.backends.cache, или используете по умолчанию get_session_auth_hash().
  • Все сообщения, если вы используете CookieStorageили FallbackStorage.
  • Все PasswordResetViewтокены.
  • Любое использование криптографической подписи, если не предоставлен другой ключ.

Если вы поверните свой секретный ключ, все вышеперечисленное будет признано недействительным. Секретные ключи не используются для паролей пользователей, и ротация ключей не повлияет на них.

Мне не было ясно, как именно я должен повернуть секретный ключ. Я нашел обсуждение того, как Django генерирует ключ для нового проекта , а также Gist, в котором обсуждаются другие варианты . В конце концов я решил просто заставить Django создать новый проект, скопировать новый секретный ключ в мой старый проект, а затем стереть новый проект .

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

Обновить

Похоже, Django добавил get_random_secret_key()функцию в версии 1.10. Вы можете использовать это для генерации нового секретного ключа.

$ ./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
$
Дон Киркби
источник
4
Генерация секретного ключа зависит от секретного ключа?
kdazzle
4
Нет, @kdazzle, если вы посмотрите на исходный кодstartproject , вы увидите, что он просто генерирует случайную строку, используя cryptoмодуль.
Дон Киркби
12
Хех, извините, @DonKirkby, плохая шутка
kdazzle
16

Согласно этой странице https://docs.djangoproject.com/en/dev/topics/signing/ , SECRET_KEY в основном используется для временных вещей - для подписи данных, отправляемых по проводам, например, для обнаружения подделки. Похоже, вещи, которые могут сломаться:

  • Подписанные файлы cookie, например значения типа «запомнить мою авторизацию на этом компьютере». В этом случае файл cookie будет признан недействительным, подпись не будет проверена, и пользователь должен будет пройти повторную проверку подлинности.
  • Для всех пользователей, которые запросили ссылки для сброса пароля или загрузки пользовательского файла, эти ссылки больше не будут действительными. Пользователи просто должны будут повторно запросить эти ссылки.

Кто-то с более недавним и / или заметным опытом Django, чем я, может вмешаться в другие, но я подозреваю, что если вы явно не делаете что-то с API подписи, это должно только создать легкое неудобство для ваших пользователей.

Тим Китинг
источник
6
Тогда почему бы не генерировать новый ключ при каждом перезапуске сервера?
osa
4
Это может вызвать проблемы, если вы используете один и тот же сервер, используя несколько процессов.
д.б.н.
1
@osa вы хотите выйти из ВСЕХ ваших пользователей каждый раз, когда вы нажимаете код / ​​перезагружаете свой сервер?
EralpB
6

Строка SECRET_KEY в основном используется для шифрования и / или хеширования данных cookie. Многие фреймворки (включая Django) приходят к этому, так как куки-файлы сеансов по умолчанию имеют свои недостатки.

Представьте, что у вас есть форма в django для редактирования статей со скрытым полем. В этом скрытом поле хранится идентификатор статьи, которую вы редактируете. И если вы хотите быть уверены, что никто не сможет отправить вам какой-либо другой идентификатор статьи, вы добавите дополнительное скрытое поле с хешированным идентификатором. Так что, если кто-то изменит идентификатор, вы узнаете об этом, потому что хэш не будет таким же.

Конечно, это тривиальный пример, но именно так используется SECRET_KEY.

Django внутренне использует его, например, для {% csrf_token%} и еще нескольких вещей. Это действительно не должно иметь никакого влияния на ваше приложение, если вы измените его, исходя из вашего вопроса и того, что вы его не используете.

Единственное, что, возможно, значения сессии будут отброшены. Так, например, пользователям придется снова войти в администратор, потому что django не сможет декодировать сеанс с другим ключом.

darkless
источник
1

Я сделал эту же ошибку. Пароль по умолчанию был длиной 50, поэтому я использовал powershell для генерации случайной строки длиной 50 и заменил на нее старый SECRET_KEY. Я вошел в систему и после замены SECRET_KEY мой предыдущий сеанс был признан недействительным.

С Powershell ( источник ):

# 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)

С Bash ( источник ):

# 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

В этот момент я подумал, почему бы не попробовать больший ключ, поэтому я попробовал его с ключом длиной 100 и 1000. Оба работали. Если я понимаю исходный код , объект, возвращаемый функцией подписи, является хэшем hmac в base64. RFC 2104 имеет это сказать для требуемой длины секретного ключа HMAC.

Приложения, использующие ключи длиной более B байтов, сначала хешируют ключ с использованием H, а затем используют результирующую L-байтовую строку в качестве фактического ключа для HMAC.

Ключ для HMAC может иметь любую длину (ключи, длина которых превышает B байтов, сначала хэшируются с использованием H). Тем не менее, настоятельно рекомендуется использовать менее L байтов, поскольку это приведет к снижению уровня безопасности функции. Ключи длиннее L байтов являются приемлемыми, но дополнительная длина не будет значительно увеличивать силу функции. (Более длинный ключ может быть полезен, если случайность ключа считается слабой.)

Чтобы перевести на обычную речь, размер секретного ключа должен совпадать с размером вывода. Ключ также должен быть в битах. Каждая цифра в base64 представляет 6 бит. Таким образом, если бы у вас был пароль из 50 символов, у вас был бы секретный ключ 50 x 6 = 300 бит. Если вы используете SHA256, то вам потребуется ключ 256 бит ( sha256 использует 256 бит по определению ). Таким образом, пароль длиной 50 должен работать, если вы не планируете использовать алгоритм хеширования больше, чем SHA256.

Но так как все дополнительные биты в ключе хэшируются, его размер не сильно снижает производительность. Но это гарантирует, что у вас достаточно битов для больших хеш-функций. SHA-512 будет охвачен длиной SECRET_KEY длиной 100 ( 50 x 6 = 600 бит> 512 бит ).

Рекс Линдер
источник