更改 Django 的 SECRET_KEY 的效果

Posted

技术标签:

【中文标题】更改 Django 的 SECRET_KEY 的效果【英文标题】:Effects of changing Django's SECRET_KEY 【发布时间】:2013-02-16 17:10:57 【问题描述】:

我犯了一个错误,将我的 Django 项目的 SECRET_KEY 提交到公共存储库中。

这个密钥应该保密according to the docs。

Django 项目已经上线,并且已经有一些活跃用户运行了一段时间。如果我更改SECRET_KEY 会有什么影响?任何现有的用户、cookie、会话等会受到影响吗?显然,新的SECRET_KEY 将不再存储在公共位置。

【问题讨论】:

【参考方案1】:

根据这个页面https://docs.djangoproject.com/en/dev/topics/signing/,SECRET_KEY 主要用于临时性的东西——例如对通过网络发送的数据进行签名,以便您可以检测到篡改。看起来可能会破坏的东西是:

签名的 cookie,例如“记住我在这台计算机上的身份验证”类型值。在这种情况下,cookie 将失效,签名将无法验证,用户将不得不重新验证。 对于请求密码重置或自定义文件下载链接的任何用户,这些链接将不再有效。用户只需重新请求这些链接。

比我有更新和/或突出 Django 经验的人可能会提出其他意见,但我怀疑除非您明确使用签名 API 做某事,否则这只会给您的用户带来轻微的不便。

【讨论】:

那为什么不在每次服务器重启时生成一个新的密钥呢? 如果您使用多个进程运行同一台服务器,这可能会导致问题。 @osa 你想在每次推送代码/重启服务器时注销所有用户吗?【参考方案2】:

SECRET_KEY 字符串主要用于加密和/或散列 cookie 数据。由于默认会话 cookie 有其自身的缺点,因此很多框架(包括 Django)都会这样做。

想象一下,您在 django 中有一个表单,用于编辑带有隐藏字段的文章。在此隐藏字段中存储您正在编辑的文章的 ID。如果您想确保没有人可以向您发送任何其他文章 ID,您将添加一个带有散列 ID 的额外隐藏字段。因此,如果有人更改 ID,您会知道,因为哈希值不会相同。

当然这是一个简单的例子,但这就是 SECRET_KEY 的使用方式。

Django 在内部使用它,例如 % csrf_token % 和其他一些东西。如果您根据您的问题和您没有使用它来更改它,那么它确实不会对您的应用程序产生任何影响。

唯一的问题是可能会话值将被丢弃。因此,例如用户将不得不再次登录管理员,因为 django 将无法使用不同的密钥解码会话。

【讨论】:

【参考方案3】:

编辑:此答案基于 django 1.5

SECRET_KEY 用在很多不同的地方,我会先指出受它影响的内容,然后尝试查看该列表并给出影响的准确解释。

直接或间接使用SECRET_KEY的事物列表:

JSON object signing crypto functions 用于盐渍 hmacs 或播种影响的随机引擎: password reset token comment form security 防止伪造的 POST 请求 form security protect against message tampering 作为消息框架可能会使用 cookie 在视图之间传递消息。 protect session data and create random session keys 以避免篡改。 create random salt for most password hashers create random passwords 如有需要 create itself when using startproject create CSRF key

实际上,这里列出的许多项目都使用SECRET_KEYdjango.utils.crypt.get_random_string(),它们使用它来播种随机引擎。这不会受到 SECRET_KEY 值更改的影响。

价值变化直接影响的用户体验有:

会话,数据解码将中断,这对任何会话后端(cookie、数据库、基于文件或缓存)都有效。 已发送的密码重置令牌将不起作用,用户必须要求一个新令牌。 cmets 表单(如果使用django.contrib.comments)将不会验证是否在值更改之前请求并在值更改之后提交。我认为这非常轻微,但可能会让用户感到困惑。 消息(来自django.contrib.messages)不会在与 cmets 表单相同的时间条件下验证服务器端。

更新:现在正在使用 django 1.9.5,快速查看源代码给了我几乎相同的答案。以后可能会进行彻底的检查。

【讨论】:

我正在更改本地开发服务器上的 SECRET_KEY 并且不会将我注销,因此更改后似乎至少会话(缓存)可以正常工作。您能否进一步详细说明data decode will break 的含义,并指出一些会中断的代码(在 django 或示例项目中)?编辑:仍在使用 django 1.4 - 是这样吗? @teferi 我不知道 1.4,这是看代码的问题。我指出了每个点的所有来源,您可以查看“保护会话数据并创建随机会话密钥”。您仍然处于登录状态是正常的,但您将无法读取会话中包含的数据,因为 SECRET_KEY 用于 salted_hmac 用于对会话数据进行哈希处理。 @Henning 我不这么认为。 auth_user 中的passwords are stored 与<algorithm>$<iterations>$<salt>$<hash> 一样,因此在每种情况下,随机盐都与密码一起存储。 在 Django 版本 > 1.5 中,答案会显着不同吗? (例如现在的 1.9) @sberder 你能根据最新的 django 版本更新答案吗?【参考方案4】:

自从提出这个问题后,Django documentation 已更改为包含答案。

密钥用于:

如果您使用除django.contrib.sessions.backends.cache 之外的任何其他会话后端,或者使用默认的get_session_auth_hash(),则为所有会话。 如果您使用的是CookieStorageFallbackStorage,则为所有消息。 所有PasswordResetView 令牌。 加密签名的任何使用,除非提供不同的密钥。

如果您轮换您的密钥,上述所有内容都将失效。密钥不用于用户的密码,密钥轮换不会影响他们。

我不清楚应该如何轮换密钥。我找到了关于 Django generates a key for a new project 的讨论,以及讨论 other options 的 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 在 1.10 版本中添加了get_random_secret_key() function。您可以使用它来生成新的密钥。

$ ./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
$

【讨论】:

秘钥生成是否依赖秘钥? 不,@kdazzle,如果您查看source code for startproject,您会发现它只是使用crypto 模块生成一个随机字符串。 嘿,对不起,@DonKirkby,开个玩笑 @kdazzle 一直是秘钥【参考方案5】:

我犯了同样的错误。默认密码是 50 长,所以我使用 powershell 生成了一个 50 长的随机字符串,并用它替换了旧的 SECRET_KEY。我已登录,更换 SECRET_KEY 后,我之前的会话已失效。

使用 Powershell (source):

# 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 (source):

# 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 长密钥。两者都有效。如果我理解source code,则签名函数返回的对象是base64 中的hmac 哈希。 RFC 2104 对所需的 HMAC 密钥长度有这样的说法。

使用密钥时间更长的应用程序 比 B 字节将首先使用 H 散列密钥,然后使用 生成的 L 字节字符串作为 HMAC 的实际密钥。

HMAC 的密钥可以是任意长度(长度超过 B 字节的密钥是 首先使用 H) 进行哈希处理。然而,小于 L 字节是强 气馁,因为它会降低安全强度 功能。长度超过 L 字节的密钥是可以接受的,但额外的 长度不会显着增加功能强度。 (一种 如果密钥的随机性为 被认为是弱者。)

要翻译成普通话,密钥的大小需要与输出的大小相同。密钥也需要以位为单位。 base64 中的每个数字代表 6 位。因此,如果您有一个 50 个字符的密码,那么您将拥有一个 50 x 6 = 300 位的密钥。如果您使用的是 SHA256,那么您需要一个 256 位密钥(sha256 根据定义使用 256 位)。因此,除非您打算使用大于 SHA256 的散列算法,否则 50 长的密码应该可以工作。

但是由于密钥中的任何额外位都会被散列,因此它的大小不会显着降低性能。但它会保证你有足够的位来处理更大的散列函数。 SHA-512 将被 100 长的 SECRET_KEY 覆盖(50 x 6 = 600 位 > 512 位)。

【讨论】:

以上是关于更改 Django 的 SECRET_KEY 的效果的主要内容,如果未能解决你的问题,请参考以下文章

Django出现Session data corrupted的原因?

Django 设置“SECRET_KEY”的目的是啥?

在 django 中是不是有生成 settings.SECRET_KEY 的功能?

django.core.exceptions.ImproperlyConfigured:设置 SECRET_KEY 环境变量

Django:配置不当:SECRET_KEY 设置不能为空

SECRET_KEY 设置不能为空 - django+pycharm