在基于 symfony 的 REST API 和 Vue.js 前端实施 XSS 预防的位置

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

我正在构建一个应用程序,该应用程序需要允许 html 标签用于 Vue.js 中的用户评论。 我不想让用户输入特定选择的 HTML 标签(p、i、ul、li)和 escape/sanitize 其他类似脚本或 div.

现在我看到了三种处理这个问题的方法:

我个人认为我们可以使用脚本或 div 等标签将数据保存到数据库中,然后在发送响应之前清理它们。

基本上我的问题是我应该在哪里实施预防措施,我应该允许像脚本这样的标签进入我的数据库吗?

如果您使用 v-html 来呈现评论,则始终存在 XSS 的可能性。严格 HTML 消毒可以降低风险,但你永远不知道。

防止 XSS 的唯一可靠方法是 永远 使用 v-htmlinnerHTML。这意味着您必须解析 HTML(使用 DOMParser)并手动呈现评论。

对于这样的事情,如果您手动编写 render function 会更容易,这样您就可以完全控制评论内容的呈现方式——只呈现您选择的 HTML 标签。白名单而不是黑名单。

不呈现用户定义的 HTML 属性。

HTML 服务器上不需要清理,因为 HTML 永远不会在浏览器中按原样呈现,但如果你想 [=48],你仍然可以清理它=] 事先的脂肪。

这是一个基本示例:

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>