230

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

根据文档,该密钥应该保密。

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

4

5 回答 5

235

编辑:这个答案基于 django 1.5

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

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

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

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

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

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

于 2013-03-13T11:16:56.033 回答
48

自从提出这个问题以来,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 在 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
$
于 2015-05-15T18:28:59.233 回答
19

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

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

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

于 2013-03-02T04:38:12.877 回答
6

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

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

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

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

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

于 2013-03-13T01:36:34.537 回答
2

我犯了同样的错误。默认密码是 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 长密钥。两者都有效。如果我了解源代码,则签名函数返回的对象是 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 bits > 512 bits)。

于 2019-11-14T06:27:54.900 回答