Do you use v-html
in your code? If so, you might be at risk of attack via HTML injection. In this article, let us show you:
As you probably know, v-html
allows you to provide a reactive string with html included which Vue will then add to the DOM as real DOM nodes (as opposed to text).
<script setup>
const html = ref('<strong>Hello</strong> world');
</script>
<template>
<p v-html="html"></p>
</template>
This differs from the mustache syntax which escapes all HTML before inserting into the DOM.
<script setup>
const html = ref('<strong>Hello</strong> world');
</script>
<template>
<p>{{ html }}</p>
</template>
Since the HTML can include any valid HTML tags, that means it has pretty much full control to do anything to your site.
For example, they could inject a style tag to turn your page background a random color.
const html = ref(`<style>body{ background: green !important }</style>`);
Or attack your page with a wall of tiled cat images or any other image they fancy (maybe a competitors logo!).
const html = ref(`<style>body{ background: url('http://whatev.com/cat') !important }</style>`);
Defacing probably isn’t good for your brand but it can get worse. Bad actors could also inject malicious scripts that do all sorts of things like:
This is known as Cross Site Scripting or XSS for short.
How is this possible with v-html
? Well, thankfully you can’t directly add a script tag into your reactive data as a string. For one, with Vite it breaks the SFC compilation but if memory serves, Vue does sanitize blatant script tags from reactive data strings. But script tags aren’t the only method of executing JavaScript.
Bad actors could use inline event listeners. Like this:
<script setup>
import { ref } from 'vue';
const html = ref(
`<img style="display:none;"
src="./a-non-existant-image.jpg"
onerror="alert('script kitties attack!')"
>`
);
</script>
<template>
<p v-html="html"></p>
</template>
So now you know 2 ways attackers could do some real damage with a poorly placed v-html
. Let’s look at a couple of examples where you might be tempted to use v-html
when you really shouldn’t.
Pretend you’re building the next greatest social media site (TwiX?) and you want to give users the power to comment with rich text. You know how to do that right? Find a nice WYSIWYG editor and give the users the power to write good ol’ HTML which you’ll then save to your database, and display for their myriad of followers…. Easy, right? wrong!
Why not?
Because the astute hacker will do exactly the dangerous things we saw above. Even if your WYSIWYG editor only supports certain tags, that’s just a client side UX concern. A malicious user can easily use devtools to send their own HTML to your server side endpoint for saving the comment.
Once the nasty comment is saved to the DB, it’s later displayed to another user of the site with v-html
and the dirty deed is done.
So the attack looks like this:
v-html
User A’s malicious code is executed in User B’s browserNow let’s pretend you’re building a landing page where you want some messaging in the hero section to differ depending on where it’s coming from. Maybe ads from Facebook show the headline “We saw you checking us out on Facebook….” and ads from YouTube say “Like watching videos? We’ve got you covered”.
To provide the most flexibility to your marketing team, you make this customizable via a url query parameter like this:
<!-- mysite.com?heroCopy=<h1>The+dynamic+text+here</h1><h2>And+a+subheadline</h2>-->
<div v-html="$route.query.heroCopy"></div>
Now we’ve got the same problem! All a malicious user has to do is get a user to click on a link where the heroCopy
query param is of their own devious choosing.
Clearly there are some ways we should NOT use v-html
but the use cases are legit. It WOULD be nice to allow users the ability to format their comments with rich text. It would be nice, to give marketing the flexibility to change out that hero copy per ad easily via the URL.
So what are some alternative approaches to these common use cases where you’re tempted to reach for v-html
?
headline
and subheadline
) as stringsFinally, let’s answer the question: When should I use v-html
?
Obviously since v-html
exists in Vue there are some legit use cases. What are they? Here’s a sampling of when it wouldn’t be a risk.
v-html
)Usually use cases for v-html
include the ability for users to provide formatted content. Think twice though before slapping v-html
on your site and calling it a day. Instead consider the consequences like site defamation and or stolen data from XSS. Then spend the bit of extra effort to reach for an alternative solution like markdown.
If you’d like to dive deeper into this topic checkout our FREE video lesson "Using v-html with User Provided Data”. It’s a part of our FREE course Common Vue.js Mistakes and How to Avoid Them.
Our goal is to be the number one source of Vue.js knowledge for all skill levels. We offer the knowledge of our industry leaders through awesome video courses for a ridiculously low price.
More than 200.000 users have already joined us. You are welcome too!
© All rights reserved. Made with ❤️ by BitterBrains, Inc.