最近遇到一个需求,在页面中展示我们拦截到别人攻击网站的攻击文本,并且高亮一些关键词
乍一看这个需求我感觉很简单啊,我拿关键词去原始数据中匹配,将需要高亮的字符串使用包裹,然后直接丢到dangerouslySetInnerHTML中展示,再针对em标签写样式,看页面效果达成,喜滋滋提交代码了😁
自测过程中发现问题,如果这段攻击文本本身就是段html代码,比如最常见的到那,直接就不会显示了,因为被html化后,这是段脚本不是普通字符串,所以我不能将整个丢到dangerouslySetInnerHTML中,而是应该分段处理,如果有em标签的,丢到angerouslySetInnerHTML中,其他就当普通字符串直接展示,所以这次修改我改成了下面这样的,心想这样应该没问题了吧,于是就提测了。
<div>
{item.value.map((it) =>
it.includes('<em>') && it.includes('</em>') ? (
<span dangerouslySetInnerHTML={{ __html: it }} />
) : (
it
)
)}
</div>
结果我低估了攻击者写攻击代码的能力,我还是太弱了😂,在测试环境我看到了这样的数据
"( \"\u0001\"|\" (filter1) (filter2) (filter3) ...) (\"!\" (filter))\u0001"
<!--#exec cmd=\"cat /flag\"-->\u0001
数据中的\u0001根本都显示不出来,我截个图就能看得比较明显了
这是浏览器response中看到的会直接是空白,如果是windows浏览器就会是个方框😭,怎么办呢,我开始了疯狂的调试之路
我们先看第二条数据,这个是要高亮 <!--#exec 这段关键词, 这个跟前面自测发现的问题类似,因为把高亮文本直接扔到了dangerouslySetInnerHTML中,所以这个就是个注释就不展示,解决这个比较简单,就手动匹配到em标签时,将高亮字符串去掉em标签再手动写em标签包裹即下面这样
it.includes('<em>') && it.includes('</em>') ? (
<em>{it.slice(4, it.length - 5)}</em> : {it}
另一个对于转义的"和特殊的字符\u0001这种就比较难办了,我试了各种办法,encode,replace,都不行直接都拦截不到,因为对于html来说"就是“, 而\u0001这种就是一个不可见字符,展示不出来😭, 我想要使用replace替换"为\", 但是拦截不到, 哎,怎么办呢,愁死了呀,于是我开启了google寻求网上大神帮助,结果没想到JSON.string竟然就能解决问题,这个东西在转成字符串的过程中就将\转义成了\, 那我只需要在展示时把两边的字符串包裹符""去掉就ok了 😄
{JSON.stringify(it).slice(1, JSON.stringify(it).length - 1 )}
提交代码查看效果果然完美解决问题。