HTML escape disabled in Vue component
ID |
javascript.vue_html_escape_disabled |
Severity |
high |
Resource |
Injection |
Language |
JavaScript |
Tags |
CWE:79, NIST.SP.800-53, OWASP:2021:A3, PCI-DSS:6.5.7, vue |
Description
Vue allows direct rendering of HTML using the v-html
attribute in templates, or innerHTML
in render functions or JSX. This is dangerous unless the passed content is sanitized against cross-site scripting (XSS) injection, or you are 100% sure that the content is safe and does not contain user input that could be passed to enable execution of JavaScript code.
Additional constructs that are potentially vulnerable to XSS injection attacks are:
-
The
v-bind:href
directive, which may be exploited withjavascript:
URLs, -
The
v-bind:style
directive. -
Event directives such as
v-on:onclick
.
And, in general, rendering part of content in <script>
or <style>
elements in Vue templates and render functions.
Rationale
Vue framework use native browser APIs, like textContent
, to render HTML content in the browser, so any JavaScript code is escaped and not executed in the browser’s JavaScript engine.
That means that in templates, the text between curly brackets like this:
<h1>{{ userData }}</h2> is 100% safe, as userData
HTML metacharacters are escaped. Attributes of HTML elements such as <h1 v-bind:title="userProvidedInput">…</h1>
are also escaped.
But developers may skip the automatic escaping by using the constructs mentioned before.
<!-- VULNERABLE -->
<div v-html="userProvided"></div>
<!-- VULNERABLE, JSX syntax in render functions -->
<div domPropsInnerHTML={this.userProvided}></div>
<!-- VULNERABLE, attacker may inject javascript: URLs
(and navigation to attacker-controlled sites is dangerous) -->
<a v-bind:href="userProvidedUrl"></a>
<script>
// call in a render function
h('div', {
domProps: {
innerHTML: this.userProvided // VULNERABLE
}
})
</script>
Remediation
If possible, do not render externally-controlled untrusted input using Vue non-escaping constructs.
By default, HTML templates and render functions in Vue automatically escape content in HTML elements and attributes.
Use {{ … }}
interpolated expressions instead.
In particular, avoid untrusted content into v-bind:href
and :onEVENT
template directives.
When rendering text using rendering functions, you should use the innerText
DOM attribute, instead of innerHtml
.
If using an external Vue component library, check how the data passed through input properties is rendered: libraries using v-html
or
even document.write()
with input properties are unsafe.
<!-- SAFE -->
<div>{{ userProvided }}</div>
<!-- SAFE (JSX syntax inside JavaScript render functions) -->
<div>{this.userProvided}</div>
<!-- SAFE if user may chose between a set of allowed fixed URLs -->
<a href="whiteListedUrl" v-if="cond_1">...</a>
<a href="whiteListedUrl2" v-else-if="cond_2">...</a>
...
<a href="whiteListedUrlN" v-else>...</a>
<script>
// call in a render function
h('div', {
domProps: {
innerText: this.userProvided // SAFE
}
// Also SAFE when written as child node
this.userProvided
})
</script>
References
-
CWE-79 : Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').
-
OWASP Top 10 2021 - A03 : Injection.