解决layui的table组件data-content未转义注入问题。

解决了Layui框架中的table组件中data-content属性没有转义html的问题。

解决layui的table组件data-content未转义注入问题。

By img Microanswer Create at:Nov 11, 2019, 11:06:18 AM 

Tags: layui table html转义 模板引擎 bug

解决了Layui框架中的table组件中data-content属性没有转义html的问题。


layui是一套非常不错的后台管理系统UI框架,其简约并具有系统感的设计赢得了许多开发者的青睐。不过我在使用layui的动态表格组件时遇到了一个问题,当数据中存在未转义的数据时,表格在渲染时会备份一个数据在dom属性上,而此数据未进行html转义导致存在注入攻击问题。我通过源码阅读,了解到了为什么此问题的原因,并完美修复这个问题。下面将进行详细介绍。

一、问题重现

并不是所有情况都会有这个问题,只有当你在为某一个字段设置了templet模板。不管是一个方法,还是一个模板语法,layui才会在此单元表格上新增一个data-content属性。而此时,如果你的数据又恰巧包含了html能解析的内容,比如:当你有一份形如下面的数据要显示到table组件里面时,由于未对data-content属性进行转义赋值,导致你的页面可能与预期展示不一样:

var data = [
    {id: 1, name: "Tom\"> <b onclick=\"alert(66)\">呵呵</b>"},
    {id: 2, name: "Bob\"> <b onclick=\"alert('你完了')\">完蛋了</b>"}
];

把这份数据通过设定templet渲染到table组件。得到下面的效果:

不仅仅是被攻击者达到了意图,而且点击它们还会弹出提示信息。再来看一看dom结构被这个数据搞成了什么样:

可以看到,由于data-content没有转义,导致界面dom结构凌乱,并且丢失了前端正常的功能。标记1处就是因为没有转义,数据里的html被应用到了dom结构里,尽管我们在templet里进行了转义使得标记2正常显示数据,但依然没能让data-content进行转义。而如果这是一个十分恶意的攻击,那么这个网站应该在这里就被攻破了。我们当然不能允许这样的事情发生。毕竟要恰饭的。

二、修复问题

修复问题的办法有好几种或者更多,这里推荐几个比较简单的解决方案。

1、后端修复

后端在数据库中将数据查询出来后,就对有些敏感字段先进行一下html转义,让前端接收到的数据已经是一个无需担心注入的安全字符串。那么这样就可以放心使用了。要在后端进行转义,下面提供一个示例代码:

String badStr = "<script>alert(66)</script>"
// 使用 Spring 提供的转换工具
String safeStr = HtmlUtils.htmlEscape(badStr);

// 现在,就可以放心的使用 safeStr 传递给前端了。

2、前端修改table组件代码

如果后端代码被多处使用,而有些"端"并不需要转换,只针对部分“端”才需要,那如果后端修复不是很方便的话,前端就不得不自行修复了。在前面也了解到,即便我们在templet里面使用了转义输出,也依然阻止不了data-content的发生。看来data-content的赋值是我们控制不了的了。

所以,不妨直接打开table组件代码,搜索data-content字段,直接定位这个赋值代码。下面代码截取自layui项目table组件[提交版本号5904e5b1344efa661b53f1cbd2a5d0e5b12ea4ef]的部分代码:

that.eachCols(function(i3, item3){
  var field = item3.field || i3
  ,key = options.index + '-' + item3.key
  ,content = item1[field];

  if(content === undefined || content === null) content = '';
  if(item3.colGroup) return;

  //td内容
  var td = ['<td data-field="'+ field +'" data-key="'+ key +'" '+ function(){ //追加各种属性
    var attr = [];
    if(item3.edit) attr.push('data-edit="'+ item3.edit +'"'); //是否允许单元格编辑
    if(item3.align) attr.push('align="'+ item3.align +'"'); //对齐方式
    if(item3.templet) attr.push('data-content="'+ content +'"'); //自定义模板
    if(item3.toolbar) attr.push('data-off="true"'); //行工具列关闭单元格事件
    if(item3.event) attr.push('lay-event="'+ item3.event +'"'); //自定义事件
    if(item3.style) attr.push('style="'+ item3.style +'"'); //自定义样式
    if(item3.minWidth) attr.push('data-minwidth="'+ item3.minWidth +'"'); //单元格最小宽度
    return attr.join(' ');
  }() +' class="'+ function(){ //追加样式

上述代码中,第14行代码处可以看到,这里直接对data-content进行赋值,完全没有进行转义的行为。所以,我们现在只需要加上转义的代码就好了:

// 原来的代码
if(item3.templet) attr.push('data-content="'+ content +'"'); //自定义模板

// 修改为:
if(item3.templet) attr.push('data-content="'+ laytpl("{{=d.t}}").render({t:content}) +'"'); //自定义模板

可以看到,上述代码中,借助了laytpl模板引擎,非常方便的就实现了数据转义后赋值到data-content里面,这样,就不会再出现注入攻击了。修改后,我们看看现在的效果以及dom结构:

现在不论是界面显示,还是dom结构,都已经显示正常。data-content也进行了转义显示,没有任何问题。

三、注意

上述在前端修复的方案,其修改的代码是layui的源代码,并非你下载的layui代码,但你可以修改好后,将修改好了的table组件代码覆盖到你下载的layui对应的组件,也可以有效果。你也可以直接修改下载的源代码。不过下载的代码是经过编译缩小的,所有字段都变成了单个字母不便于识别,修改时要小心,找准位置和代码,使用同样的方式进行修复,不过要注意,这时laytpl可能就是另一个字母变量了。

Full text complete, Reproduction please indicate the source. Help you? Not as good as one:
Comment(Comments need to be logged in. You are not logged in.)
You need to log in before you can comment.

Comments (0 Comments)