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

建议 未结 1 464
Microanswer
Microanswer 2019-11-11
悬赏:20飞吻
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转义,让前端接收到的数据已经是一个无需担心注入的安全字符串。那么这样就可以放心使用了。要在后端进行转义,下面提供一个示例java代码:

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]的部分代码:
01. that.eachCols(function(i3, item3){
02. var field = item3.field || i3
03. ,key = options.index + '-' + item3.key
04. ,content = item1[field];
05.
06. if(content === undefined || content === null) content = '';
07. if(item3.colGroup) return;
08.
09. //td内容
10. var td = ['<td data-field="'+ field +'" data-key="'+ key +'" '+ function(){ //追加各种属性
11. var attr = [];
12. if(item3.edit) attr.push('data-edit="'+ item3.edit +'"'); //是否允许单元格编辑
13. if(item3.align) attr.push('align="'+ item3.align +'"'); //对齐方式
14. if(item3.templet) attr.push('data-content="'+ content +'"'); //自定义模板
15. if(item3.toolbar) attr.push('data-off="true"'); //行工具列关闭单元格事件
16. if(item3.event) attr.push('lay-event="'+ item3.event +'"'); //自定义事件
17. if(item3.style) attr.push('style="'+ item3.style +'"'); //自定义样式
18. if(item3.minWidth) attr.push('data-minwidth="'+ item3.minWidth +'"'); //单元格最小宽度
19. return attr.join(' ');
20. }() +' 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`可能就是另一个字母变量了。

本文原文地址: 解决layui的table组件data-content未转义导致的注入问题。
回帖
  • 这是个坑
    还得改个地方

    在后台管理中出现HTML标签的数据很正常吧!
    0 回复