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

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

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

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

【问题讨论】:

    标签: django


    【解决方案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的事物列表:

      实际上,这里列出的许多项目都使用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 位)。

      【讨论】:

        猜你喜欢
        • 2017-04-12
        • 1970-01-01
        • 2021-06-18
        • 2015-08-26
        • 1970-01-01
        • 2011-11-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多