揭开Flask app.secret_key的神秘面纱

MYV*_*MYV 109 python flask web

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

这就是烧瓶用户指南对该主题所说的一切.

我对Web开发很陌生,我不知道任何安全性的工作原理是什么/为什么.我想了解Flask在幕后做的事情.

  • 为什么Flask强迫我们设置这个secret_key属性?
  • Flask如何使用该secret_key物业?

Mar*_*ers 89

任何需要加密的东西(为了安全防止攻击者的篡改)都需要设置密钥.对于刚刚瓶本身,即"什么"是Session对象,但其他的扩展可以使用相同的秘密的.

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

快速入门中的 " 会话"部分对您应设置的服务器端机密类型提供了良好而明智的建议.

加密依赖于秘密; 如果您没有为加密设置服务器端密码,那么每个人都可以破解加密; 它就像是你电脑的密码.秘密加上数据到符号用于创建签名字符串,使用加密散列算法创建难以重建的值; 只有当你拥有完全相同的秘密并且原始数据可以重新创建这个值时,让Flask检测是否有任何未经许可的更改.由于Flask发送给客户端的数据从不包含秘密,因此客户端无法篡改会话数据并希望生成新的有效签名.

Flask使用该itsdangerous来完成所有艰苦的工作; 会话使用具有自定义JSON序列化程序的itsdangerous.URLSafeTimedSerializer.


Mic*_*oka 57

以下答案主要涉及签名Cookie,即会话概念的实现(在Web应用程序中使用).Flask提供普通(未签名)cookie(通过request.cookiesresponse.set_cookie())和签名cookie(通过flask.session).答案分为两部分,第一部分描述如何生成签名Cookie,第二部分以QA的形式呈现,解决方案的不同方面.用于示例的语法是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')
Run Code Online (Sandbox Code Playgroud)

(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
Run Code Online (Sandbox Code Playgroud)

(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....
Run Code Online (Sandbox Code Playgroud)

(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
Run Code Online (Sandbox Code Playgroud)

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

# add cookie to response
>>> response.set_cookie(cookie)
# send to browser --> 
Run Code Online (Sandbox Code Playgroud)

从浏览器收到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)
Run Code Online (Sandbox Code Playgroud)

(6)使用原始cookie和应用程序SECRET_KEY使用与步骤3中相同的方法重新计算签名.

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

(7)将计算结果与先前从刚收到的cookie中弹出的签名进行比较.如果它们匹配,我们就知道cookie没有被搞砸了.但即使只是在cookie中添加了空格,签名也不会匹配.

# if both signatures match, your cookie has not been modified
>>> good_cookie = popped_signature==calculated_signature
Run Code Online (Sandbox Code Playgroud)

(8)如果它们不匹配,那么您可以回复任意数量的操作,记录事件,丢弃cookie,发出新的,重定向到登录页面等.

>>> if not good_cookie:
...     security_log(cookie)
Run Code Online (Sandbox Code Playgroud)

基于哈希的消息认证码(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()
Run Code Online (Sandbox Code Playgroud)

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....
Run Code Online (Sandbox Code Playgroud)

两个哈希都是相同的,你知道你有一个相同的副本.

什么是饼干?

关于cookie的广泛讨论将超出这个问题的范围.我在这里提供了一个概述,因为最小的理解对于更好地理解如何以及为什么SECRET_KEY有用是有用的.我强烈建议您跟进一些有关HTTP Cookie的个人阅读材料.

Web应用程序中的常见做法是将客户端(Web浏览器)用作轻量级缓存.Cookie是这种做法的一种实现方式.Cookie通常是服务器通过其标头添加到http响应的一些数据.它由浏览器保存,然后在发出请求时通过http标头将其发送回服务器.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
Run Code Online (Sandbox Code Playgroud)

从任何现代浏览器中浏览Cookie都是微不足道的.例如,在Firefox上,转到首选项>隐私>历史记录>删除单个cookie.

content字段与应用程序最相关.其他字段主要包含元指令以指定各种影响范围.

为什么要使用cookies?

简短的回答是表现.使用cookie,最大限度地减少了在各种数据存储(内存缓存,文件,数据库等)中查找内容的需要,从而加快了服务器应用程序的速度.请记住,cookie越大,网络上的有效负载越重,因此您在服务器上的数据库查找中可能会节省您在网络上丢失的内容.仔细考虑您的cookie中包含哪些内容.

为什么需要签署cookie?

Cookie用于保存各种信息,其中一些信息可能非常敏感.它们本质上也是不安全的,并且要求对双方,客户和服务器采取一些辅助预防措施以任何方式保证安全.签署cookie专门解决了他们在试图欺骗服务器应用程序时可能会遇到的问题.还有其他措施来缓解其他类型的漏洞,我鼓励您阅读更多有关cookie的信息.

如何篡改cookie?

Cookie以文本形式驻留在客户端上,可以毫不费力地进行编辑.您的服务器应用程序收到的cookie可能由于多种原因而被修改,其中一些可能不是无辜的.想象一下,一个Web应用程序可以在cookie上保留有关其用户的权限信息,并根据该信息授予权限.如果cookie没有修补,那么任何人都可以修改他们的状态,将他们的状态从"role = visitor"升级到"role = admin",而应用程序将更加明智.

为什么SECRET_KEY需要签署cookie?

验证cookie与按照前面描述的方式验证源代码有点不同.在源代码的情况下,原始作者是参考指纹(校验和)的受托人和所有者,其将被公开.您不信任的是源代码,但您信任公共签名.因此,要验证源代码的副本,您只需要计算的哈希值与公共哈希值匹配.

然而,在cookie的情况下,应用程序不跟踪签名,它会跟踪它SECRET_KEY.这SECRET_KEY是参考指纹.Cookies旅行时带有他们声称合法的签名.这里的合法性意味着签名是由cookie的所有者(即应用程序)发出的,在这种情况下,它声称您不信任,并且您需要检查签名的有效性.要做到这一点,你需要在签名中包含一个只有你知道的元素,就是那个SECRET_KEY.有人可能会更改cookie,但由于他们没有正确计算有效签名的秘密成分,因此无法对其进行欺骗.如前所述,这种类型的指纹识别,其中在校验和的顶部也提供了密钥,称为消息认证码.

塞申斯怎么样?

其经典实现中的会话是仅在该content字段中携带ID的cookie session_id.会话的目的与签名的cookie完全相同,即防止cookie被篡改.然而,古典会议有不同的方法.在接收到会话cookie时,服务器使用该ID在其自己的本地存储器中查找会话数据,该存储器可以是数据库,文件,或者有时是存储器中的缓存.会话cookie通常设置为在浏览器关闭时到期.由于本地存储查找步骤,会话的这种实现通常会导致性能损失.签名cookie正在成为首选替代方案,这就是Flask会话的实施方式.换句话说,Flask会话签名cookie,并且在Flask中使用签名cookie只需使用其SessionAPI.

为什么不加密cookie?

有时,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'
Run Code Online (Sandbox Code Playgroud)

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

如果不使用随机生成的密钥,您可以使用复杂的单词,数字和符号,也可以排列在只有您自己知道的句子中,以字节形式编码.

不要设置SECRET_KEY与生成每个这就是所谓的时间不同的键的功能直接.例如,不要这样做:

# this is not good
SECRET_KEY = random_key_generator()
Run Code Online (Sandbox Code Playgroud)

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

而是打开一个交互式python shell并调用该函数来生成密钥,然后将其复制并粘贴到配置中.

  • @HugoSousa 关于保持密钥不变的解释在“如果我更改 SECRET_KEY 会发生什么?”部分中给出。关于如何保持应用程序配置安全的讨论虽然有用,但是一个单独的主题,我认为超出了范围。但我鼓励任何有同样担忧的人在该特定方向上寻求建议。 (2认同)