问题:解密Flask app.secret_key
如果 app.secret_key
未设置,则Flask将不允许您设置或访问会话字典。
我对Web开发非常陌生,我不知道任何/为什么安全性工作原理。我想了解Flask在幕后所做的事情。
- 为什么Flask强迫我们设置此
secret_key
属性? - Flask如何使用该
secret_key
物业?
回答 0
任何需要加密的内容(为了安全防范攻击者的篡改)都需要设置密钥。对于刚刚瓶本身,即“什么”是Session
对象,但其他的扩展可以使用相同的秘密的。
secret_key
仅仅是为SECRET_KEY
配置密钥设置的值,或者您可以直接设置它。
快速入门中的 “ 会话”部分对应设置哪种服务器端机密提供了很好的建议。
加密取决于机密;如果您没有设置要使用的加密服务器端密码,那么每个人都可以破坏您的加密;就像您计算机的密码一样。秘密加上要签名的数据用于创建签名字符串,使用加密哈希算法很难重新创建值;仅当您具有完全相同的机密且原始数据时,您才能重新创建此值,让Flask检测是否未经许可对任何内容进行了更改。由于Flask永远不会将秘密包含在发送给客户端的数据中,因此客户端无法篡改会话数据,并希望产生新的有效签名。
Flask使用该itsdangerous
库来完成所有艰苦的工作;会话使用带有自定义JSON序列化程序的itsdangerous.URLSafeTimedSerializer
类。
回答 1
以下答案主要与Signed Cookies有关,后者是会话概念的实现(在Web应用程序中使用)。Flask同时提供普通(未签名)Cookie(通过request.cookies
和response.set_cookie()
)和签名Cookie(via flask.session
)。答案有两个部分,第一部分描述了如何生成签名Cookie,第二部分以解决方案不同方面的QA形式呈现。这些示例使用的语法是Python3,但是这些概念也适用于以前的版本。
什么是SECRET_KEY
(或如何创建签名Cookie)?
签署cookie是防止cookie篡改的预防措施。在对Cookie进行签名的过程中,SECRET_KEY
使用方式类似于在对哈希进行哈希处理之前使用“盐”来混淆密码的方式。这是对该概念的(疯狂的)简化描述。示例中的代码是说明性的。许多步骤已被省略,并非实际上所有功能都存在。这里的目的是提供对一般概念的理解,实际的实现将更多地涉及到。另外,请记住,Flask在后台为您完成了大部分操作。因此,除了(通过会话API)为cookie设置值并提供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)现在将签名粘贴content
在原始cookie字段的一端。
# 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
步骤3相同的方法,将原始cookie与应用程序一起使用以重新计算签名。
# 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没有被弄乱。但是,即使只是在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()
SECRET_KEY
:) 的“神秘化”
在这种情况下,什么是“签名”?
这是确保除未经授权的个人或实体以外的任何人未修改某些内容的方法。
签名的最简单形式之一是“ 校验和 ”,它简单地验证两个数据是否相同。例如,从源代码安装软件时,重要的是首先确认您的源代码副本与作者的副本相同。一种常用的方法是通过加密哈希函数运行源,并将输出与项目主页上发布的校验和进行比较。
举例来说,假设您要从网络镜像以压缩文件的形式下载项目的源代码。在该项目的网页上发布的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?
关于cookie的广泛讨论将超出此问题的范围。我在这里提供了一个概述,因为对基础知识的最低了解对于更好地了解如何以及为什么SECRET_KEY
有用很有用。我强烈建议您跟进一些有关HTTP Cookie的个人阅读。
Web应用程序中的一种常见做法是将客户端(Web浏览器)用作轻量级缓存。Cookies是这种做法的一种实现。Cookie通常是服务器通过其标头添加到HTTP响应的一些数据。它由浏览器保存,然后在发出请求时也通过HTTP标头将其发送回服务器。Cookie中包含的数据可用于模拟所谓的有状态性,即服务器正在与客户端保持持续连接的错觉。仅在这种情况下,您无需为保持连接“活动”而进行连接,而仅在处理了客户端的请求后才拥有应用程序状态的快照。这些快照在客户端和服务器之间来回传送。收到请求后,服务器首先读取cookie的内容,以重新建立与客户端的对话上下文。然后,它在该上下文中处理请求,然后将响应返回给客户端,然后更新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
从任何现代浏览器中都可以细读Cookie。例如,在Firefox上,转到“首选项”>“隐私”>“历史记录”>删除单个cookie。
该content
字段与应用程序最相关。其他字段大多带有元指令,以指定各种影响范围。
为什么要使用Cookie?
简短的答案是性能。使用cookie可以最大程度地减少在各种数据存储(内存缓存,文件,数据库等)中查找内容的需求,从而可以在服务器应用程序方面加快查找速度。请记住,cookie越大,网络上的有效负载就越重,因此您在服务器上的数据库查找中保存的内容可能会通过网络丢失。请仔细考虑您的Cookie中要包含的内容。
Cookies用于保存各种信息,其中某些信息可能非常敏感。从本质上讲,它们也不安全,并且要求采取许多辅助预防措施,无论对于客户端还是服务器双方,都应以任何方式视为安全。签名cookie专门解决了在欺骗服务器应用程序时可能对其进行修补的问题。还有其他措施可以缓解其他类型的漏洞,我建议您阅读有关Cookie的更多信息。
Cookie如何被篡改?
Cookies以文本形式驻留在客户端上,可以轻松进行编辑。您的服务器应用程序收到的Cookie可能由于多种原因而被修改,其中某些原因可能并非无辜。想象一个Web应用程序在cookie上保留有关其用户的许可信息,并根据该信息授予特权。如果该cookie不可靠,则任何人都可以修改其cookie,以将其状态从“ role = visitor”提升到“ role = admin”,并且该应用程序再合适不过了。
为什么
SECRET_KEY
需要签署Cookie?
验证cookie与之前描述的验证源代码有点不同。对于源代码,原始作者是参考指纹(校验和)的受托人和所有者,参考指纹将保持公开状态。您不信任的是源代码,但是您信任公共签名。因此,要验证您的源副本,您只希望您计算出的哈希值与公共哈希值匹配。
对于Cookie,应用程序不跟踪签名,而是跟踪SECRET_KEY
。的SECRET_KEY
是参考指纹。Cookies随身携带其声称合法的签名。此处的合法性意味着签名是由Cookie的所有者(即应用程序)发布的,在这种情况下,这是声称您不信任并且需要检查签名的有效性。为此,您需要在签名中包括一个只有您自己知道的元素,即SECRET_KEY
。有人可以更改Cookie,但是由于他们没有秘密成分来正确计算有效签名,因此无法欺骗它。如前所述,这种类型的指纹识别在校验和的顶部还提供了一个秘密密钥,
会话呢?
传统实现中的会话是cookie,该cookie在content
字段中仅携带一个ID session_id
。会话的目的与签名的cookie完全相同,即防止cookie篡改。古典会议有不同的方法。收到会话cookie后,服务器将使用ID在其自己的本地存储中查找会话数据,该本地存储可以是数据库,文件,有时还可以是内存中的缓存。会话cookie通常设置为在关闭浏览器时过期。由于存在本地存储查找步骤,因此会话的这种实现通常会导致性能下降。签名cookie正在成为首选,这就是Flask会话的实现方式。换句话说,Flask会话对是Cookie 进行了签名,要在Flask中使用签名的Cookie,只需使用其Session
API。
有时,在对Cookie的内容进行签名之前,可以对其进行加密。如果认为它们太敏感而无法从浏览器中看到它们,则可以执行此操作(加密将隐藏内容)。但是,仅对cookie进行签名就解决了另一种需求,即希望在浏览器上保持一定程度的可见性和可用性,同时又避免被cookie所干扰。
如果我改变了会
SECRET_KEY
怎样?
通过更改,SECRET_KEY
您将使所有使用前一个密钥签名的cookie 失效。当应用程序接收到一个请求,该请求包含一个使用前一个签名的cookie时,SECRET_KEY
它将尝试使用新的签名计算签名SECRET_KEY
,并且两个签名都不匹配,该cookie及其所有数据将被拒绝,就像浏览器是第一次连接到服务器。用户将被注销,他们的旧Cookie和内部存储的所有内容都将被遗忘。请注意,这与处理过期Cookie的方式不同。如果过期的cookie的签名签出,则可以延长其租约。无效的签名仅表示一个普通的无效cookie。
因此,除非您想使所有已签名的Cookie失效,否则请尝试SECRET_KEY
长时间保持相同。
有什么好处
SECRET_KEY
?
密钥应该很难猜到。有关Sessions的文档为随机密钥生成提供了一个很好的方法:
>>> 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()
每次重新启动应用程序时,都会获得一个新密钥,从而使之前的密钥失效。
而是打开一个交互式python shell并调用该函数以生成密钥,然后将其复制并粘贴到配置中。