[Vuejs]-Where to implement XSS prevention in symfony-based REST API and Vue.js front-end

0👍

If you’re using v-html to render the comments, then there’s always the possibility of XSS. Strict HTML sanitization can mitigate the risk, but you never know.

The only surefire way to prevent XSS is to never use v-html or innerHTML. This means you’ll have to parse the HTML (using DOMParser) and render the comments manually.

For something like this it will be easier if you write the render function manually so you have full control over how the comment content will be rendered – only render the HTML tags you choose. Whitelist instead of blacklist.

Don’t render user-defined HTML attributes.

HTML sanitization won’t be necessary on the server because the HTML will never be rendered as-is in the browser, but you can still sanitize it if you want to trim the fat beforehand.

Here’s a basic example:

Vue.component('comment-content', {
  functional: true,
  
  props: {
    html: {},
    allowedElements: {
      default: () => ['p', 'i', 'b', 'ul', 'li'],
    },
  },
  
  render(h, ctx) {
    const { html, allowedElements } = ctx.props;
  
    const renderNode = node => {
      switch (node.nodeType) {
        case Node.TEXT_NODE: return renderTextNode(node);
        case Node.ELEMENT_NODE: return renderElementNode(node);
      }
    };
    
    const renderTextNode = node => {
      return node.nodeValue;
    };
    
    const renderElementNode = node => {
      const tag = node.tagName.toLowerCase();
      if (allowedElements.includes(tag)) {
        const children = [...node.childNodes].map(node => renderNode(node));
        return h(tag, children);
      }
    };
    
    const parser = new DOMParser();
    const doc = parser.parseFromString(html, 'text/html');
    return [...doc.body.childNodes].map(node => renderNode(node));
  },
});

new Vue({
  el: '#app',
  data: {
    html: `
      <p>Paragraph</p>
      <ul>
        <li>One <script>alert('Hacked')<\/script></li>
        <li onmouseover="alert('Hacked')">Two</li>
        <li style="color: red">Three <b>bold</b> <i>italic</i></li>
        <li>Four <img src="javascript:alert('Hacked')"></li>
      </ul>
      <section>This element isn't allowed</section>
      <p>Last paragraph</p>
    `,
  },
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <comment-content :html="html"></comment-content>
</div>

Leave a comment