Close
升级到 Vue 3 | Vue 2 EOL

安全

报告漏洞

当漏洞被报告时,它会立即成为我们的首要关注点,全职贡献者会放下所有工作来处理它。要报告漏洞,请发送电子邮件至 security@vuejs.org

虽然发现新漏洞的情况很少见,但我们也建议始终使用最新版本的 Vue 及其官方配套库,以确保您的应用程序尽可能安全。

规则 1:绝不使用不可信的模板

使用 Vue 时最基本的安全规则是 **绝不使用不可信的内容作为您的组件模板**。这样做等同于允许在您的应用程序中执行任意 JavaScript 代码 - 更糟糕的是,如果代码在服务器端渲染期间执行,可能会导致服务器被入侵。此类用法的示例

new Vue({
el: '#app',
template: `<div>` + userProvidedString + `</div>` // NEVER DO THIS
})

Vue 模板被编译成 JavaScript,模板中的表达式将在渲染过程中执行。虽然表达式是针对特定渲染上下文进行评估的,但由于潜在的全局执行环境的复杂性,对于像 Vue 这样的框架来说,在不产生不切实际的性能开销的情况下,完全屏蔽您免受潜在的恶意代码执行是不切实际的。完全避免此类问题的最直接方法是确保您的 Vue 模板的内容始终是可信的,并且完全由您控制。

Vue 为您做了什么

HTML 内容

无论使用模板还是渲染函数,内容都会自动转义。这意味着在这个模板中

<h1>{{ userProvidedString }}</h1>

如果 userProvidedString 包含

'<script>alert("hi")</script>'

那么它将被转义为以下 HTML

&lt;script&gt;alert(&quot;hi&quot;)&lt;/script&gt;

从而防止脚本注入。这种转义是使用原生浏览器 API(如 textContent)完成的,因此漏洞只能在浏览器本身存在漏洞的情况下才会出现。

属性绑定

类似地,动态属性绑定也会自动转义。这意味着在这个模板中

<h1 v-bind:title="userProvidedString">
hello
</h1>

如果 userProvidedString 包含

'" onclick="alert(\'hi\')'

那么它将被转义为以下 HTML

&quot; onclick=&quot;alert('hi')

从而防止关闭 title 属性以注入新的、任意的 HTML。这种转义是使用原生浏览器 API(如 setAttribute)完成的,因此漏洞只能在浏览器本身存在漏洞的情况下才会出现。

潜在的危险

在任何 Web 应用程序中,允许未经清理的用户提供的内容作为 HTML、CSS 或 JavaScript 执行都是潜在的危险,因此应尽可能避免。但是,有时一些风险是可以接受的。

例如,像 CodePen 和 JSFiddle 这样的服务允许用户提供的内容被执行,但它是在一个上下文中,在这个上下文中,这是预期的,并且在一定程度上被沙盒化在 iframe 中。在某些情况下,一项重要功能本质上需要一定程度的漏洞,您的团队需要权衡该功能的重要性与漏洞可能带来的最坏情况。

注入 HTML

正如您之前了解到的,Vue 会自动转义 HTML 内容,防止您意外地将可执行的 HTML 注入到您的应用程序中。但是,在您知道 HTML 是安全的情况下,您可以显式地渲染 HTML 内容

请注意,用户提供的 HTML 永远不能被认为是 100% 安全的,除非它位于沙盒化的 iframe 中,或者在应用程序的某个部分,只有编写该 HTML 的用户才能接触到它。此外,允许用户编写自己的 Vue 模板会带来类似的危险。

注入 URL

在这样的 URL 中

<a v-bind:href="userProvidedUrl">
click me
</a>

如果 URL 没有被“清理”以防止使用 javascript: 执行 JavaScript 代码,则存在潜在的安全问题。有一些库,例如 sanitize-url 可以帮助您解决这个问题,但请注意

如果您在前端进行 URL 清理,那么您已经存在安全问题。用户提供的 URL 应该始终由您的后端在保存到数据库之前进行清理。这样,就可以避免所有连接到您的 API 的客户端(包括原生移动应用程序)出现此问题。还要注意,即使使用清理后的 URL,Vue 也无法帮助您保证它们会引导到安全的目的地。

注入样式

看看这个例子

<a
v-bind:href="sanitizedUrl"
v-bind:style="userProvidedStyles"
>
click me
</a>

假设 sanitizedUrl 已经过清理,因此它绝对是一个真实的 URL,而不是 JavaScript。使用 userProvidedStyles,恶意用户仍然可以提供 CSS 来进行“点击劫持”,例如,将链接的样式设置为透明框,覆盖“登录”按钮。然后,如果 https://user-controlled-website.com/ 被构建成类似于您应用程序的登录页面,那么他们可能已经捕获了用户的真实登录信息。

您可以想象,允许用户为 <style> 元素提供内容会造成更大的漏洞,让该用户完全控制如何对整个页面进行样式设置。这就是为什么 Vue 阻止在模板中渲染样式标签,例如

<style>{{ userProvidedStyles }}</style>

为了让您的用户完全免受点击劫持,我们建议只允许在沙盒化的 iframe 中完全控制 CSS。或者,当通过样式绑定提供用户控制时,我们建议使用其 对象语法,并且只允许用户为他们可以安全控制的特定属性提供值,例如

<a
v-bind:href="sanitizedUrl"
v-bind:style="{
color: userProvidedColor,
background: userProvidedBackground
}"
>
click me
</a>

注入 JavaScript

我们强烈建议不要使用 Vue 渲染 <script> 元素,因为模板和渲染函数不应该有副作用。但是,这不是包含在运行时被评估为 JavaScript 的字符串的唯一方法。

每个 HTML 元素都有属性,其值接受 JavaScript 字符串,例如 onclickonfocusonmouseenter。将用户提供的 JavaScript 绑定到这些事件属性中的任何一个都是潜在的安全风险,因此应该避免。

请注意,用户提供的 JavaScript 永远不能被认为是 100% 安全的,除非它位于沙盒化的 iframe 中,或者在应用程序的某个部分,只有编写该 JavaScript 的用户才能接触到它。

有时我们会收到关于如何在 Vue 模板中进行跨站点脚本 (XSS) 的漏洞报告。一般来说,我们不认为这些情况是真正的漏洞,因为没有实际的方法可以保护开发人员免受允许 XSS 的两种情况的影响

  1. 开发人员明确要求 Vue 将用户提供的、未经清理的内容作为 Vue 模板进行渲染。这本身是不安全的,Vue 无法知道其来源。

  2. 开发人员将 Vue 安装到整个 HTML 页面,该页面恰好包含服务器端渲染的和用户提供的内容。这与 #1 基本上是同一个问题,但有时开发人员可能会在没有意识到的情况下这样做。这会导致可能的漏洞,攻击者提供作为纯 HTML 安全但作为 Vue 模板不安全的 HTML。最佳实践是永远不要将 Vue 安装到可能包含服务器端渲染的和用户提供的内容的节点上。

最佳实践

一般来说,如果你允许未经消毒的用户提供的内容被执行(作为 HTML、JavaScript 甚至 CSS),你可能会让自己面临攻击。无论使用 Vue、其他框架还是根本不使用框架,这条建议都适用。

除了上面针对 潜在危险 的建议外,我们还建议你熟悉以下资源:

然后,利用你学到的知识,检查依赖项的源代码,看看是否存在潜在的危险模式,尤其是那些包含第三方组件或影响 DOM 渲染的依赖项。

后端协调

HTTP 安全漏洞,例如跨站请求伪造 (CSRF/XSRF) 和跨站脚本包含 (XSSI),主要在后端解决,因此不是 Vue 的问题。但是,与后端团队沟通以了解如何最好地与他们的 API 交互仍然是一个好主意,例如,在表单提交时提交 CSRF 令牌。

服务器端渲染 (SSR)

使用 SSR 时,存在一些额外的安全问题,因此请务必遵循 我们的 SSR 文档 中概述的最佳实践,以避免漏洞。