安全注意事项¶
Web 应用程序通常会面临各种安全问题,而且很难做到万无一失。Flask 尝试为你解决其中一些问题,但还有一些问题需要你自己解决。
跨站脚本 (XSS)¶
跨站脚本是指将任意 HTML(以及 JavaScript)注入到网站上下文中。为了解决此问题,开发人员必须正确转义文本,使其无法包含任意 HTML 标记。有关更多信息,请参阅维基百科中关于跨站脚本的文章。
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 注入,攻击者甚至可以使元素填满整个页面,这样用户只需将鼠标放在页面上的任何位置即可触发攻击。
有一种 Jinja 转义无法防御的 XSS 问题。a
标签的 href
属性可以包含 javascript: URI,如果不妥善保护,浏览器在单击时会执行该 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", "")
大多数现代终端在粘贴时都会警告并删除隐藏字符,因此这不是严格必要的。还可以通过其他无法过滤的方式来构建危险命令。根据您网站的用例,最好对复制代码发出警告。