在基于 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.
现在我看到了三种处理这个问题的方法:
- 使用 Vue.js
渲染内容
- 在 Symfony 中发送响应之前(我正在使用 JMS 序列化器)
- 收到请求后 API
我个人认为我们可以使用脚本或 div 等标签将数据保存到数据库中,然后在发送响应之前清理它们。
基本上我的问题是我应该在哪里实施预防措施,我应该允许像脚本这样的标签进入我的数据库吗?
如果您使用 v-html
来呈现评论,则始终存在 XSS 的可能性。严格 HTML 消毒可以降低风险,但你永远不知道。
防止 XSS 的唯一可靠方法是 永远 使用 v-html
或 innerHTML
。这意味着您必须解析 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>
我正在构建一个应用程序,该应用程序需要允许 html 标签用于 Vue.js 中的用户评论。 我不想让用户输入特定选择的 HTML 标签(p、i、ul、li)和 escape/sanitize 其他类似脚本或 div.
现在我看到了三种处理这个问题的方法:
- 使用 Vue.js 渲染内容
- 在 Symfony 中发送响应之前(我正在使用 JMS 序列化器)
- 收到请求后 API
我个人认为我们可以使用脚本或 div 等标签将数据保存到数据库中,然后在发送响应之前清理它们。
基本上我的问题是我应该在哪里实施预防措施,我应该允许像脚本这样的标签进入我的数据库吗?
如果您使用 v-html
来呈现评论,则始终存在 XSS 的可能性。严格 HTML 消毒可以降低风险,但你永远不知道。
防止 XSS 的唯一可靠方法是 永远 使用 v-html
或 innerHTML
。这意味着您必须解析 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>