安全注意事项 — Flask 文档

来自菜鸟教程
Flask/docs/1.1.x/security
跳转至:导航、​搜索

安全注意事项

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 将在警告窗口中呈现给用户。 但是,一个好的攻击者可能会执行任何其他 JavaScript 代码,而不是向用户显示 cookie。 结合 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 与每个请求一起发送到页面。 不幸的是,这包括由 3rd 方网站触发的请求。 如果您不牢记这一点,有些人可能会用社交工程欺骗您的应用程序用户,在他们不知情的情况下做一些愚蠢的事情。

假设您有一个特定的 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'


X-XSS-保护

如果请求包含类似 JavaScript 的内容并且响应包含相同的数据,浏览器将尝试通过不加载页面来防止反射型 XSS 攻击。

response.headers['X-XSS-Protection'] = '1; mode=block'


HTTP 公钥固定 (HPKP)

这会告诉浏览器仅使用特定的证书密钥向服务器进行身份验证,以防止 MITM 攻击。

警告

启用此功能时要小心,因为如果您错误地设置或升级密钥,将很难撤消。



复制/粘贴到终端

隐藏字符,如退格字符 (\b^H) 可能导致文本在 HTML 中呈现与将 粘贴到终端 时的解释方式不同。

例如,import y\bose\bm\bi\bt\be\b 在 HTML 中呈现为 import yosemite,但在粘贴到终端时应用退格键,变成 import os

如果您希望用户从您的站点复制和粘贴不受信任的代码,例如用户在技术博客上发表的评论,请考虑应用额外的过滤,例如替换所有 \b 字符。

body = body.replace("\b", "")

大多数现代终端在粘贴时都会警告并删除隐藏字符,因此这不是绝对必要的。 也可以以其他无法过滤的方式制作危险命令。 根据您网站的使用案例,通常显示有关复制代码的警告可能会很好。