解密 Flask app.secret _ key

如果没有设置 app.secret_key,Flask 将不允许您设置或访问会话字典。

这是所有的 烧瓶用户指南必须说在这个问题上。

我是一个网络开发新手,我不知道如何/为什么任何安全的东西工作。我想知道弗拉斯克在引擎盖下做了什么。

  • 为什么 Flask 强迫我们设置这个 secret_key属性?
  • Flask 如何使用 secret_key属性?
96564 次浏览

任何需要加密的东西(用于防止攻击者篡改的安全保管)都需要设置密钥。对于 只是 Flask 本身,“ anything”是 Session对象,但其他扩展可以利用同样的秘密。

secret_key仅仅是为 SECRET_KEY配置键设置的值,或者您可以直接设置它。

关于应该设置什么样的服务器端机密,快速启动中的会话部分提供了很好的、明智的建议。

加密依赖于机密; 如果你没有设置一个服务器端的机密供加密使用,每个人都可以破解你的加密; 它就像你电脑的密码。秘密加上数据到签名被用来创建一个签名字符串,一个使用 加密哈希算法加密哈希算法很难重新创建的值; 只有当你有完全相同的秘密 还有原始数据时,你才能重新创建这个值,让 Flask 检测是否有任何未经许可的改变。由于这个秘密从未包含在 Flask 发送给客户端的数据中,因此客户端不能篡改会话数据,并希望生成一个新的、有效的签名。

Flask 使用 itsdangerous完成所有繁重的工作; 会话使用带有定制 JSON 序列化器的 itsdangerous.URLSafeTimedSerializer

下面的答案主要与 签名饼干有关,它是 会议概念的一个实现(在 Web 应用程序中使用)。烧瓶提供两个,正常(未签名)的 Cookie (通过 request.cookiesresponse.set_cookie())和签名 Cookie (通过 flask.session)。答案分为两部分: 第一部分描述如何生成 Signed Cookie,第二部分以一系列 问题/答案的形式提供,这些 问题/答案处理方案的不同方面。示例使用的语法是 Python3,但是这些概念也适用于以前的版本。

什么是 SECRET_KEY(或如何创建签名 Cookie) ?

签署 Cookie 是防止 Cookie 篡改的一种预防措施。在签名 cookie 的过程中,使用 SECRET_KEY的方式类似于在散列之前使用“ salt”来混淆密码。这里有一个(广泛)简化的概念描述。示例中的代码是为了说明问题。许多步骤被省略了,并非所有的函数都实际存在。这里的目标是提供对主要思想的一般理解,但是实际的实现可能会更加复杂一些。另外,请记住 Flask 已经在后台为您提供了大部分内容。因此,除了为 cookie 设置值(通过会话 API)和提供一个 SECRET_KEY之外,自己重新实现它不仅是不明智的,而且也没有必要这样做:

一个穷人的饼干签名

在向浏览器发送响应之前:

(1)首先建立 SECRET_KEY。应该只有应用程序知道,并且应该在应用程序的生命周期(包括通过应用程序重新启动)中保持相对稳定。

# choose a salt, a secret string of bytes
>>> SECRET_KEY = 'my super secret key'.encode('utf8')

(2)创建一个 cookie

>>> cookie = make_cookie(
...     name='_profile',
...     content='uid=382|membership=regular',
...     ...
...     expires='July 1 2030...'
... )


>>> print(cookie)
name: _profile
content: uid=382|membership=regular...
...
...
expires: July 1 2030, 1:20:40 AM UTC

(3)为了创建签名,将 SECRET_KEY附加(或预置)到 cookie 字节串,然后从该组合生成散列。

# encode and salt the cookie, then hash the result
>>> cookie_bytes = str(cookie).encode('utf8')
>>> signature = sha1(cookie_bytes+SECRET_KEY).hexdigest()
>>> print(signature)
7ae0e9e033b5fa53aa....

(4)现在将签名贴在原始 cookie 的 content字段的一端。

# include signature as part of the cookie
>>> cookie.content = cookie.content + '|' + signature
>>> print(cookie)
name: _profile
content: uid=382|membership=regular|7ae0e9...  <--- signature
domain: .example.com
path: /
send for: Encrypted connections only
expires: July 1 2030, 1:20:40 AM UTC

这就是发送给客户的内容。

# add cookie to response
>>> response.set_cookie(cookie)
# send to browser -->

从浏览器收到 Cookie 后:

(5)当浏览器将这个 Cookie 返回给服务器时,从 Cookie 的 content字段中去掉签名以返回原始 Cookie。

# Upon receiving the cookie from browser
>>> cookie = request.get_cookie()
# pop the signature out of the cookie
>>> (cookie.content, popped_signature) = cookie.content.rsplit('|', 1)

(6)使用与应用程序的 SECRET_KEY一起使用的原始 cookie,使用与步骤3相同的方法重新计算签名。

# recalculate signature using SECRET_KEY and original cookie
>>> cookie_bytes = str(cookie).encode('utf8')
>>> calculated_signature = sha1(cookie_bytes+SECRET_KEY).hexdigest()

(7)将计算结果与刚收到的 cookie 中先前弹出的签名进行比较。如果它们匹配,我们就知道饼干没有被动过手脚。但是,即使只向 cookie 添加了一个空格,签名也不会匹配。

# if both signatures match, your cookie has not been modified
>>> good_cookie = popped_signature==calculated_signature

(8)如果它们不匹配,那么你可以用任意数量的操作来响应,记录事件,丢弃 cookie,发布一个新的 cookie,重定向到一个登录页面,等等。

>>> if not good_cookie:
...     security_log(cookie)

金钥杂凑讯息鑑别码(HMAC)

上面生成的需要密钥以确保某些内容完整性的签名类型在加密中称为 讯息鑑别码MAC

我在前面指出,上面的示例过于简化了这个概念,实现您自己的签名并不是一个好主意。这是因为在 Flask 中用于签名 cookie 的算法被称为 HMAC,它比上述简单的一步一步的算法要复杂一些。总体思路是相同的,但是由于超出本文讨论范围的原因,这一系列的计算稍微复杂一些。 如果你仍然对 DIY 感兴趣,通常情况下,Python 有一些模块可以帮助你开始:)这里有一个起点:

import hmac
import hashlib


def create_signature(secret_key, msg, digestmod=None):
if digestmod is None:
digestmod = hashlib.sha1
mac = hmac.new(secret_key, msg=msg, digestmod=digestmod)
return mac.digest()

HmacHashlib的文档。


SECRET_KEY的“去神秘化”:)

在这种情况下,什么是“签名”?

这是一种确保某些内容没有被任何人修改的方法,除了一个人或一个被授权这样做的实体。

最简单的签名形式之一是“ 校对”,它只是验证两段数据是否相同。例如,在从源代码安装软件时,首先要确认源代码的副本与作者的副本相同,这一点很重要。一种常见的方法是通过一个密码杂凑函数运行源代码,并将输出与项目主页上发布的校验和进行比较。

例如,假设您要从 Web 镜像下载一个项目的源代码到一个 gzip 文件中。在该项目的网页上发布的 SHA1校验和是‘ eb84e8da7ca23e9f83... ...’

# so you get the code from the mirror
download https://mirror.example-codedump.com/source_code.tar.gz
# you calculate the hash as instructed
sha1(source_code.tar.gz)
> eb84e8da7c....

两个散列是相同的,你知道你有一个相同的副本。

饼干是什么?

关于 cookie 的广泛讨论将超出这个问题的范围。我在这里提供一个概述,因为对 SECRET_KEY如何以及为什么有用的最低限度的理解可能是有用的。我强烈建议您对 HTTP Cookies 进行一些个人阅读。

Web 应用程序中的一个常见实践是使用客户端(Web 浏览器)作为轻量级缓存。Cookies 是这种实践的一种实现。Cookie 通常是服务器通过其头部向 HTTP 响应添加的一些数据。它由浏览器保存,浏览器随后在发出请求时将其发送回服务器,也是通过 HTTP 头的方式。Cookie 中包含的数据可以用来模拟所谓的 有声有色,即服务器与客户端保持持续连接的错觉。只是,在这种情况下,不需要连接来保持连接“活跃”,只需要在应用程序处理了客户机请求之后获得应用程序状态的快照即可。这些快照在客户机和服务器之间来回传送。在接收到请求后,服务器首先读取 Cookie 的内容,以重新建立与客户端会话的上下文。然后,它在该上下文中处理请求,并在将响应返回给客户端之前更新 cookie。因此,会议正在进行的假象得以维持。

饼干长什么样?

一个典型的曲奇应该是这样的:

name: _profile
content: uid=382|status=genie
domain: .example.com
path: /
send for: Encrypted connections only
expires: July 1 2030, 1:20:40 AM UTC

Cookies 对于任何现代浏览器来说都是琐碎的,比如在 Firefox 上可以访问 参考资料 > 私隐 > 历史记录 > 删除个别 Cookie

content字段是与应用程序最相关的字段。其他字段大多带有元指令,以指定不同的影响范围。

为什么要用饼干呢?

简单来说就是绩效。使用 cookie,最大限度地减少了在各种数据存储(内存缓存、文件、数据库等)中查找内容的需要,从而加快了服务器应用程序方面的工作。请记住,cookie 越大,网络上的有效负载就越重,因此在服务器上的数据库查找中保存的内容可能会在网络上丢失。仔细考虑在你的饼干中加入什么。

为什么饼干需要签名?

Cookie 用于保存各种信息,其中一些信息可能非常敏感。它们本质上也是不安全的,并且需要采取一些辅助预防措施,以任何方式对双方、客户端和服务器都被认为是安全的。签署 Cookie 特别解决了这样一个问题,即在试图欺骗服务器应用程序时,它们可以被修补。还有其他一些措施可以减轻其他类型的漏洞,我鼓励你阅读更多关于 cookie 的内容。

饼干怎么会被篡改呢?

Cookie 以文本形式驻留在客户端上,可以不费吹灰之力进行编辑。您的服务器应用程序接收到的 Cookie 可能由于多种原因而被修改,其中一些原因可能不是无辜的。想象一下,一个 Web 应用程序在 cookie 上保存其用户的权限信息,并根据这些信息授予特权。如果这个 cookie 不能修补,那么任何人都可以修改他们的 cookie,将他们的状态从“ role = viser”提升到“ role = admin”,而应用程序不会察觉到这一点。

为什么签署 cookie 需要 SECRET_KEY

验证 Cookie 与验证源代码的方式稍有不同。在源代码的情况下,原始作者是参考指纹(校验和)的受托人和所有者,它将保持公开。您不信任的是源代码,但您信任公共签名。因此,要验证源的副本,只需要计算出的哈希与公共哈希匹配即可。

然而,对于 cookie,应用程序并不跟踪签名,而是跟踪其 SECRET_KEYSECRET_KEY是参考指纹。饼干的签名是合法的。这里的合法性意味着签名是由 cookie 的所有者签发的,也就是应用程序,在这种情况下,就是你不信任的声明,你需要检查签名的有效性。为此,您需要在签名中包含一个只有您知道的元素,即 SECRET_KEY。有些人可能会改变一个饼干,但由于他们没有正确计算有效签名的秘密成分,他们不能欺骗它。如前所述,这种类型的指纹识别,在校验和之上还提供了一个密钥,被称为讯息鑑别码。

那塞申斯呢?

经典实现中的会话是只在 content字段 session_id中带有 ID 的 cookie。会话的目的与签名 Cookie 完全相同,即防止 Cookie 篡改。不过,经典课程有不同的方法。在接收到会话 cookie 后,服务器使用 ID 在其本地存储中查找会话数据,这些本地存储可以是数据库、文件,有时也可以是内存中的缓存。会话 Cookie 通常设置为在浏览器关闭时过期。由于本地存储查找步骤,这种会话实现通常会导致性能下降。签名 cookie 正在成为首选的替代方案,这就是 Flask 会话的实现方式。换句话说,Flask 会话 签名 Cookie,在 Flask 中使用签名 Cookie 只需使用它的 SessionAPI。

为什么不同时对 cookie 进行加密呢?

有时 Cookie 的内容可以在 也正在被签署之前加密。如果它们被认为过于敏感而不能从浏览器中看到(加密隐藏了内容) ,就会这样做。然而,简单地签署 cookies 就可以满足一种不同的需求,即希望在浏览器上保持一定程度的 cookie 可见性和可用性,同时防止它们被干涉。

如果我改变 SECRET_KEY会发生什么?

通过更改 SECRET_KEY,您将使用前一个密钥签名的 所有 cookie 无效。当应用程序接收到一个使用之前的 SECRET_KEY签名的 cookie 的请求时,它将尝试计算使用新的 SECRET_KEY的签名,如果两个签名不匹配,这个 cookie 及其所有数据将被拒绝,就好像浏览器第一次连接到服务器一样。用户将被注销,他们的旧 cookie 将被遗忘,以及任何存储在里面的东西。请注意,这与处理过期 Cookie 的方式不同。如果签名检查正确,过期 cookie 的租约可能会被延长。无效的签名只意味着一个普通的无效 cookie。

因此,除非您想使所有签名的 cookie 无效,否则请尝试在较长时间内保持 SECRET_KEY相同。

什么是良好的 SECRET_KEY

密钥应该是很难猜到的,会议上的文档有一个随机密钥生成的好方法:

>>> import os
>>> os.urandom(24)
'\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O<!\xd5\xa2\xa0\x9fR"\xa1\xa8'

复制密钥并将其作为 SECRET_KEY的值粘贴到配置文件中。

除了使用随机生成的密钥之外,您还可以使用复杂的词、数字和符号组合,也许排列在只有您知道的句子中,以字节形式进行编码。

执行 没有直接使用一个函数设置 SECRET_KEY,该函数在每次调用时生成一个不同的键。例如,不要这样做:

# this is not good
SECRET_KEY = random_key_generator()

每次重新启动应用程序时,都会给它一个新密钥,从而使前一个密钥无效。

相反,打开一个交互式 pythonshell 并调用函数来生成密钥,然后将其复制并粘贴到配置中。