首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >富文本场景下的 XSS

富文本场景下的 XSS

作者头像
madneal
发布2021-09-24 15:46:35
发布2021-09-24 15:46:35
2.6K0
举报
文章被收录于专栏:madMenmadMen

富文本编辑器是一个常见的业务场景,一般来说,通过富文本编辑器编辑的内容最终也会 html 的形式来进行渲染,比如 VUE,一般就会使用 v-html 来承载富文本编辑的内容。因为文本内容需要通过 html 来进行渲染,那么显然普通的编码转义不适合这种场景了,因为这样最终的呈现的效果就不是我们想要的了。针对于这种场景,显然过滤是唯一的解决方案了,不过过滤其实可以在后端和前端都是可以做的,后端做的话,一般是在数据存储在数据库之前。前端做的话,则是在数据最终在页面渲染之前做过滤。

前端的过滤方案,可以尝试使用开源的 [js-xss](https://github.com/leizongmin/js-xss)。先介绍一下这个库的使用方法,这个库可以在 nodejs 中使用,同样也可以在浏览中直接引入使用。

代码语言:javascript
复制
// nodejs 中使用var xss = require("xss");var html = xss('<script>alert("xss");</script>');console.log(html);
代码语言:javascript
复制
// 浏览器中使用<script src="https://rawgit.com/leizongmin/js-xss/master/dist/xss.js"></script><script>  // apply function filterXSS in the same way  var html = filterXSS('<script>alert("xss");</scr' + "ipt>");  alert(html);</script>

一般在 vue 的项目中,通过 webpack 也可以直接通过 CommonJS 的方式引入,与 nodejs 的引入方式基本一致。值得注意的一个问题是,默认情况下会去禁用 style 属性,这样会导致富文本的样式展示异常,需要禁用 css 过滤或者使用白名单的方式来进行过滤。

代码语言:javascript
复制
const xssFilter = new xss.FilterXSS({    css: false});html = xssFilter.process('<script>alert("xss");</script>');
代码语言:javascript
复制
const xssFilter = new xss.FilterXSS({  css: {    whiteList: {      position: /^fixed|relative$/,      top: true,      left: true,    },  },});html = xssFilter.process('<script>alert("xss");</script>');

其实 js-xss 的原理并不是很复杂,如果去扒一下源码,原理其实主要就是实现标签和属性的白名单过滤,这样的方案简单有效。因为默认配置了大部分标签以及属性的白名单方案,所以一般可以做到开箱即用,当然如果有定制化的需求需要进一步定制化函数。

代码语言:javascript
复制
function getDefaultWhiteList() {  return {    a: ["target", "href", "title"],    abbr: ["title"],    address: [],    area: ["shape", "coords", "href", "alt"],    article: [],    aside: [],    audio: [      "autoplay",      "controls",      "crossorigin",      "loop",      "muted",      "preload",      "src",    ],    b: [],    bdi: ["dir"],    bdo: ["dir"],    big: [],    blockquote: ["cite"],    br: [],    caption: [],    center: [],    cite: [],    code: [],    col: ["align", "valign", "span", "width"],    colgroup: ["align", "valign", "span", "width"],    dd: [],    del: ["datetime"],    details: ["open"],    div: [],    dl: [],    dt: [],    em: [],    figcaption: [],    figure: [],    font: ["color", "size", "face"],    footer: [],    h1: [],    h2: [],    h3: [],    h4: [],    h5: [],    h6: [],    header: [],    hr: [],    i: [],    img: ["src", "alt", "title", "width", "height"],    ins: ["datetime"],    li: [],    mark: [],    nav: [],    ol: [],    p: [],    pre: [],    s: [],    section: [],    small: [],    span: [],    sub: [],    summary: [],    sup: [],    strong: [],    strike: [],    table: ["width", "border", "align", "valign"],    tbody: ["align", "valign"],    td: ["width", "rowspan", "colspan", "align", "valign"],    tfoot: ["align", "valign"],    th: ["width", "rowspan", "colspan", "align", "valign"],    thead: ["align", "valign"],    tr: ["rowspan", "align", "valign"],    tt: [],    u: [],    ul: [],    video: [      "autoplay",      "controls",      "crossorigin",      "loop",      "muted",      "playsinline",      "poster",      "preload",      "src",      "height",      "width",    ],  };}

另外前端过滤的时机一般是选择数据在页面渲染之前。在 vue 中,选择在 created() 做过滤即可。不过在 JS 中有一种绕过过滤的方案,就是在过滤函数之前让 JS 报错,那么这样过滤函数就不会执行了,从而导致绕过。

这么看来,在数据储存之前,后端做过滤也不失为一个稳妥的方案。因为公司是以 golang 为主的技术栈,就讨论一下 golang 方面的技术方案。bluemonday 是一款 golang 的 HTML 过滤器,相对于 js-xss 来说,这个库的可定制性更高。

基于默认的过滤策略:

代码语言:javascript
复制
Hello <STYLE>.XSS{background-image:url("javascript:alert('XSS')");}</STYLE><A CLASS=XSS></A>World

会被过滤为

Hello World

而对于:

代码语言:javascript
复制
<a href="http://www.google.com/">  <img src="https://ssl.gstatic.com/accounts/ui/logo_2x.png"/></a>

大部分内容不会变化,只是给 a 标签增加了一个 rel 属性,更安全。

代码语言:javascript
复制
<a href="http://www.google.com/" rel="nofollow">  <img src="https://ssl.gstatic.com/accounts/ui/logo_2x.png"/></a>

默认的策略使用 bluemonday 非常方便:

代码语言:javascript
复制
package mainimport (  "fmt"  "github.com/microcosm-cc/bluemonday")func main() {  p := bluemonday.UGCPolicy()  html := p.Sanitize("<a onblur="alert(secret)" href="http://www.google.com">Google</a>")  fmt.Println(html)}

另外定制性真的特别强大,语义性好,傻瓜式入门,可以便捷地自定义过滤策略。

代码语言:javascript
复制
p := bluemonday.NewPolicy()// 标签白名单p.AllowElements("b", "strong")// 正则表达式白名单p.AllowElementMatch(regex.MustCompile("^my-element-"))

其实从原理上来说,bluemonday 与 js-xss 并没有本质的区别,主要就是基于标签和属性的过滤,可以基于自己的技术场景去选择。不过记得一点是两种方案过滤时机的选择。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-09-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 madMen 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档