安全注意事项¶
Web 应用程序面临许多潜在的安全问题,并且可能很难完全正确地处理所有事情,甚至不知道通常情况下“正确”是什么。Flask 默认尝试解决其中一些问题,但还有其他部分您可能需要自行处理。许多这些解决方案都是权衡,并将取决于每个应用程序的具体需求和威胁模型。许多托管平台可能会处理某些类型的问题,而无需 Flask 应用程序来处理。
资源使用¶
一种常见的攻击类别是“拒绝服务”(DoS 或 DDoS)。这是一个非常广泛的类别,不同的变体针对已部署应用程序中的不同层。一般来说,会采取一些措施来增加处理每个请求所用的处理时间或内存,直到没有足够的资源来处理合法请求。
Flask 提供了一些配置选项来处理资源使用。它们也可以在单个请求上设置,以仅自定义该请求。每个选项的文档都更详细地介绍了。
MAX_CONTENT_LENGTH
或Request.max_content_length
控制将从请求中读取多少数据。默认情况下未设置,但除非 WSGI 服务器指示支持,否则它仍会阻止真正无限的流。MAX_FORM_MEMORY_SIZE
或Request.max_form_memory_size
控制任何非文件multipart/form-data
字段可以有多大。默认设置为 500kB。MAX_FORM_PARTS
或Request.max_form_parts
控制可以解析多少multipart/form-data
字段。默认设置为 1000。结合默认的max_form_memory_size
,这意味着一个表单最多占用 500MB 的内存。
无论这些设置如何,您还应查看您的操作系统、容器部署(Docker 等)、WSGI 服务器、HTTP 服务器和托管平台提供的设置。它们通常有方法来设置进程资源限制、超时和其他检查,而与 Flask 的配置方式无关。
跨站脚本(XSS)¶
跨站脚本是指将任意 HTML(以及 JavaScript)注入到网站上下文中的概念。为了解决这个问题,开发人员必须正确转义文本,使其不能包含任意 HTML 标签。有关更多信息,请查看 Wikipedia 上关于 跨站脚本 的文章。
Flask 配置 Jinja2 自动转义所有值,除非明确告知不要这样做。这应该排除模板中造成的所有 XSS 问题,但仍有一些其他地方您必须小心
在没有 Jinja2 帮助的情况下生成 HTML
在用户提交的数据上调用
Markup
从上传的文件中发送 HTML,永远不要这样做,使用
Content-Disposition: attachment
标头来防止该问题。从上传的文件中发送文本文件。一些浏览器正在使用基于前几个字节的内容类型猜测,因此用户可能会欺骗浏览器执行 HTML。
另一件非常重要的事情是未加引号的属性。虽然 Jinja2 可以通过转义 HTML 来保护您免受 XSS 问题的影响,但有一件事它无法保护您免受攻击:通过属性注入进行 XSS。为了应对这种可能的攻击媒介,请务必在使用 Jinja 表达式时始终用双引号或单引号引用您的属性
<input value="{{ value }}">
为什么有必要这样做?因为如果您不这样做,攻击者可以轻松注入自定义 JavaScript 处理程序。例如,攻击者可以注入这段 HTML+JavaScript
onmouseover=alert(document.cookie)
当用户随后将鼠标移动到输入框上时,cookie 将在警报窗口中呈现给用户。但是,一个好的攻击者可能不仅会向用户显示 cookie,还可能执行任何其他 JavaScript 代码。结合 CSS 注入,攻击者甚至可以使元素填充整个页面,以便用户只需将鼠标放在页面上的任何位置即可触发攻击。
有一类 XSS 问题是 Jinja 的转义无法防范的。a
标签的 href
属性可以包含 javascript:
URI,如果未正确保护,浏览器会在单击时执行它。
<a href="{{ value }}">click here</a>
<a href="javascript:alert('unsafe');">click here</a>
为了防止这种情况,您需要设置 内容安全策略 (CSP) 响应头。
跨站请求伪造(CSRF)¶
另一个大问题是 CSRF。这是一个非常复杂的主题,我不会在这里详细概述,只是提及它是什么以及如何在理论上预防它。
如果您的身份验证信息存储在 cookie 中,则您具有隐式状态管理。“已登录”状态由 cookie 控制,并且该 cookie 随每个请求发送到页面。不幸的是,这包括由第三方站点触发的请求。如果您不记住这一点,有些人可能会通过社会工程学来欺骗您的应用程序用户,让他们在不知情的情况下做蠢事。
假设您有一个特定的 URL,当您向其发送 POST
请求时,将删除用户的个人资料(例如 http://example.com/user/delete
)。如果攻击者现在创建一个页面,该页面使用一些 JavaScript 向该页面发送 post 请求,他们只需欺骗一些用户加载该页面,他们的个人资料最终将被删除。
想象一下,如果您要运行 Facebook,拥有数百万并发用户,并且有人会发出指向小猫图片的链接。当用户访问该页面时,他们的个人资料将在他们观看毛茸茸的猫的图片时被删除。
您如何防止这种情况发生?基本上,对于每个修改服务器内容的请求,您都必须使用一次性令牌,并将其存储在 cookie 中 并且 也随表单数据一起传输。在服务器上再次收到数据后,您将必须比较这两个令牌并确保它们相等。
为什么 Flask 不为您做这件事?发生这种情况的理想位置是表单验证框架,而 Flask 中不存在表单验证框架。
JSON 安全性¶
在 Flask 0.10 及更低版本中,jsonify()
不会将顶级数组序列化为 JSON。这是因为 ECMAScript 4 中存在安全漏洞。
ECMAScript 5 关闭了这个漏洞,因此只有极少数非常旧的浏览器仍然容易受到攻击。所有这些浏览器都有 其他更严重的漏洞,因此更改了此行为,并且 jsonify()
现在支持序列化数组。
安全标头¶
浏览器识别各种响应标头以控制安全性。我们建议您查看以下每个标头,以便在您的应用程序中使用。可以使用 Flask-Talisman 扩展来管理 HTTPS 和安全标头。
HTTP 严格传输安全(HSTS)¶
告诉浏览器将所有 HTTP 请求转换为 HTTPS,从而防止中间人(MITM)攻击。
response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
内容安全策略(CSP)¶
告诉浏览器可以从哪里加载各种类型的资源。应尽可能使用此标头,但需要一些工作来为您的站点定义正确的策略。一个非常严格的策略是
response.headers['Content-Security-Policy'] = "default-src 'self'"
X-Content-Type-Options¶
强制浏览器遵守响应内容类型,而不是尝试检测它,这可能会被滥用以生成跨站脚本(XSS)攻击。
response.headers['X-Content-Type-Options'] = 'nosniff'
X-Frame-Options¶
防止外部站点将您的站点嵌入到 iframe
中。这可以防止一类攻击,其中外部框架中的点击可以隐形地转换为点击您页面上的元素。这也称为“点击劫持”。
response.headers['X-Frame-Options'] = 'SAMEORIGIN'
HTTP 公钥固定(HPKP)¶
这告诉浏览器仅使用特定的证书密钥与服务器进行身份验证,以防止 MITM 攻击。
警告
启用此功能时请小心,因为如果您设置或错误地升级了密钥,则很难撤消。
复制/粘贴到终端¶
隐藏字符(例如退格字符(\b
,^H
))可能导致文本在 HTML 中的呈现方式与 粘贴到终端 中时的解释方式不同。
例如,import y\bose\bm\bi\bt\be\b
在 HTML 中呈现为 import yosemite
,但退格符在粘贴到终端时会被应用,它会变成 import os
。
如果您希望用户从您的网站复制和粘贴不受信任的代码,例如从技术博客上用户发布的评论中复制和粘贴,请考虑应用额外的过滤,例如替换所有 \b
字符。
body = body.replace("\b", "")
大多数现代终端会在粘贴时警告并删除隐藏字符,因此这并非绝对必要。也可以通过其他无法过滤的方式来制作危险命令。根据您网站的用例,最好显示有关复制代码的总体警告。