前端页面展示\" ,\u0001等特殊转义字符

363 阅读2分钟

最近遇到一个需求,在页面中展示我们拦截到别人攻击网站的攻击文本,并且高亮一些关键词

乍一看这个需求我感觉很简单啊,我拿关键词去原始数据中匹配,将需要高亮的字符串使用包裹,然后直接丢到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根本都显示不出来,我截个图就能看得比较明显了

截屏2023-09-25 下午4.55.58.png 这是浏览器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 )}

提交代码查看效果果然完美解决问题。