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

根据文件,这把钥匙应该是保密的。

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


当前回答

因为提出了这个问题,Django文档已经更改为包含一个答案。

The secret key is used for: All sessions if you are using any other session backend than django.contrib.sessions.backends.cache, or are using the default get_session_auth_hash(). All messages if you are using CookieStorage or FallbackStorage. All PasswordResetView tokens. Any usage of cryptographic signing, unless a different key is provided. If you rotate your secret key, all of the above will be invalidated. Secret keys are not used for passwords of users and key rotation will not affect them.

我不清楚该如何旋转密钥。我找到了一个关于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在1.10版本中添加了get_random_secret_key()函数。您可以使用它来生成一个新的密钥。

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

其他回答

编辑:这个答案是基于django 1.5的

SECRET_KEY被用于许多不同的地方,我将首先指出它的影响,然后尝试浏览该列表并给出影响的精确解释。

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

JSON object signing crypto functions for salted hmacs or seeding the random engine which impacts: password reset token comment form security to protect against forged POST requests form security protect against message tampering as the message framework may use cookies to pass messages between views. protect session data and create random session keys to avoid tampering as well. create random salt for most password hashers create random passwords if necessary create itself when using startproject create CSRF key

实际上,这里列出的很多项都通过django.utils.crypt.get_random_string()使用SECRET_KEY来为随机引擎播种。这不会受到SECRET_KEY值更改的影响。

受价值变化直接影响的用户体验包括:

sessions, the data decode will break, that is valid for any session backend (cookies, database, file based or cache). password reset token already sent won't work, users will have to ask a new one. comments form (if using django.contrib.comments) will not validate if it was requested before the value change and submitted after the value change. I think this is very minor but might be confusing for the user. messages (from django.contrib.messages) won't validate server-side in the same timing conditions as for comments form.

更新:现在在django 1.9.5上工作,快速看一下源代码给了我几乎相同的答案。之后可能会做个彻底的检查。

我也犯了同样的错误。默认密码是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长的钥匙。这两个工作。如果我理解源代码,signer函数返回的对象是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位)。因此,50长的密码应该可以工作,除非你计划使用大于SHA256的哈希算法。

但是由于键中的任何额外位都被散列,它的大小不会大幅降低性能。但它能保证你有足够的比特来处理更大的哈希函数。SHA-512将由一个100长的SECRET_KEY (50 x 6 = 600位> 512位)覆盖。

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

假设你在django中有一个表单,用于编辑带有隐藏字段的文章。在这个隐藏字段中存储您正在编辑的文章的ID。如果你想确保没有人可以发送任何其他文章id给你,你会添加一个额外的隐藏字段与哈希id。如果有人改变ID,你会知道,因为哈希值不一样了。

当然,这是一个简单的示例,但这是如何使用SECRET_KEY的。

Django内部使用它,例如{% csrf_token %}和其他一些东西。如果您根据您的问题和您不使用它而更改它,它真的不应该对您的应用程序产生任何影响。

唯一的问题是会话值可能会被删除。例如,用户将不得不再次登录到admin,因为django将无法用不同的密钥解码会话。

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

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

一些比我更有Django经验的人可能会对此表示赞同,但我怀疑,除非您明确地使用签名API,否则这只会给用户带来轻微的不便。

因为提出了这个问题,Django文档已经更改为包含一个答案。

The secret key is used for: All sessions if you are using any other session backend than django.contrib.sessions.backends.cache, or are using the default get_session_auth_hash(). All messages if you are using CookieStorage or FallbackStorage. All PasswordResetView tokens. Any usage of cryptographic signing, unless a different key is provided. If you rotate your secret key, all of the above will be invalidated. Secret keys are not used for passwords of users and key rotation will not affect them.

我不清楚该如何旋转密钥。我找到了一个关于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在1.10版本中添加了get_random_secret_key()函数。您可以使用它来生成一个新的密钥。

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