关于DOM中转义字符的处理规则
作者:秋了秋 发表时间:2021年11月02日
浏览器为什么转译?
浏览器为了保护自身语言的安全,会将别人给它的非html标记语言(但包含了浏览器预留的符号)自动转成实体字符,以防html语言被这些符号破坏结构。比如<的实体符号是<<的实体字符是>。如果不转译,下面的内容会显示什么?
<div>b<a吗?</div>
最后浏览器编译后:
<div> b <a吗?< div> </a吗?<> </div>
浏览器会把a吗?</div整个当做一个标签,导致本该显示的内容没有显示出来。至于/为什么显示成了空格,这是它的另一个机制,还有自动填补结束标签的机制暂不概述。
看看浏览器怎么处理这种字符:
document.body.innerText = '<abc>';
最终浏览器插入dom中的数据会变成:
<body> <abc> </body>
这是浏览器的自我保护动作
除非你使用的就是强制要插入dom的函数innerHTML:
document.body.innerHTML = '<abc>';
最终浏览器插入dom中的数据会变成:
<body> <abc></abc> </body>
一些常见特殊符号
<>'"&
程序员主动转译的意义
这得从xss攻击说起,如果这些字符不转译的话,则会当成html语法执行,最经典的执行标签是script, 浏览器会把script引入或者包裹的代码当做js来执行。情景模式:
无聊的人在网站发表评论,评论里面带有这种脚本,一旦评论成功则意味着这个评论在网络上任何人都可见,只要别人访问了带有这个评论的网页,而网页又没有做转译的输出,那么代码将会在访问者浏览器执行。脚本可以做很多事情,包括访问这个网站的登录cookie, 再通过网络把访问到的信息发送出去,则无聊的人则会收到这些私密信息,访问者将会处于不安全处境,随时可能异地登录。所以转译这种特殊字符还可以防跨站脚本攻击。
什么时候需要编解码?
当使用浏览器的api(innerText、setAttribute)添加数据的时候不需要编解码, 当使用字符拼接的时候往浏览器塞东西的时候需要编码,如:
var title = htmlEncode('<script>alert(123);<\/script>'); document.body.innerHTML = '<span>'+title+'</span>';
转译的方式
这里介绍两种转译的方式:
1.一种是replace硬替换:
//转译 function htmlEncode(text) { var specialMap = { "<": "<", ">": ">", "\"": """, "'": "'", } for(let key in specialMap) { if(specialMap.hasOwnProperty(key)) { text = text.replace(new RegExp(key, 'g'), specialMap[key]); } } text = text.replace(/&(?!lt;|gt;|quot;|apos;|amp;|#039;)/, '&'); return text; } //反转译 function htmlDecode(text) { var specialMap = { "&": "&", "<": "<", ">": ">", """: "\"", "'": "'", } for(let key in specialMap) { if(specialMap.hasOwnProperty(key)) { text = text.replace(new RegExp(key, 'g'), specialMap[key]); } } return text; }
2.另一种是借助浏览器的api来做:
//转译 function htmlEncode(text) { var div = document.createElement('div'); if(div.textContent !== undefined) { div.textContent = text; } else { div.innerText = text; } return div.innerHTML; } //反转译 function htmlDecode(text) { var div = document.createElement('div'); div.innerHTML = text; if(div.textContent !== undefined) { return div.textContent; } else { return div.innerText; } }
优缺点很显然,
第一种不受环境影响,客户端、内存、worker、服务端都可以使用,拎到哪用到哪。
第二种依赖浏览器运行环境,但是维护成本低,哪怕浏览器更改了这个协议也不需要改代码。
以上转译你不需要担心改变了用户想要看到的结果,对于界面上的显示,浏览器的渲染还是会呈现出字符原本的模样,除非以下的场景(需要手动反转译):
1.输入框,如input、textarea、数据层面导出excel等文件。
input.value = htmlDecode(text);
业界标准的做法是前端输入框输入的任何东西都必须转译特殊字符保存到数据库,从数据库读取展示到页面上的时候依情况是否需要反转译。