Где реализовать предотвращение XSS в API REST на основе Symfony и в интерфейсе Vue.js - PullRequest
0 голосов
/ 07 декабря 2018

Я создаю приложение, которое требует использования html-тегов для комментариев пользователей в Vue.js.Я не хочу, чтобы пользователи могли вводить определенный набор HTML-тегов (p, i, ul, li) и экранировать / дезинфицировать другие, такие как script или div.

Сейчас я вижу три способа борьбы с этимпроблема:

  • При рендеринге содержимого с помощью Vue.js
  • Перед отправкой ответа в Symfony (я использую JMS Serializer)
  • После получения запроса кAPI

Лично я думаю, что мы могли бы сохранить данные в базе данных с помощью таких тегов, как script или div, и просто санировать их перед отправкой ответа.

По сути, мой вопрос: где я долженреализовать меры предосторожности и разрешить ли в моей базе данных теги типа script?

1 Ответ

0 голосов
/ 07 декабря 2018

Если вы используете v-html для визуализации комментариев, то всегда есть возможность XSS.Строгая санация HTML может снизить риск, но вы никогда не знаете.

Единственный надежный способ предотвратить XSS - это никогда использовать v-html или innerHTML.Это означает, что вам придется анализировать HTML (используя DOMParser ) и визуализировать комментарии вручную.

Для чего-то подобного вам будет проще, если вы напишите render render вручную, чтобы иметь полный контроль над тем, как будет отображаться содержимое комментария, - только те HTML-теги, которые вы выбрали.Белый список вместо черного списка.

Не отображать пользовательские атрибуты HTML.

Очистка HTML на сервере не требуется, поскольку в браузере HTML никогда не будет отображаться как есть., но вы все равно можете продезинфицировать его, если хотите заранее обрезать жир.

Вот базовый пример:

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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...