前端开发中的字符编码详解

 

成都创新互联公司专业成都网站建设、网站建设,集网站策划、网站设计、网站制作于一体,网站seo、网站优化、网站营销、软文平台等专业人才根据搜索规律编程设计,让网站在运行后,在搜索中有好的表现,专业设计制作为您带来效益的网站!让网站建设为您创造效益。

前端开发过程中会接触各种各样的编码,比较常见的主要是UTF-8和HTML实体编码,但是web前端的世界却不止这两种编码,而且编码的选择也会 造成一定的问题,如前后端开发过程中不同编码的兼容、多字节编码可能会造成的XSS漏洞等。因此,本文旨在更好的全面了解涉及前端开发领域的字符编码,避 免可能出现的交互和开发中的忽视的漏洞。

URL编码

我曾经在URL编码解码和base64一文中讲述了URL编码中的三组函数,并对比了这三组函数与base64编码的关系,在此简要说明一下。
escape/unescape函数针对宽字符做unicode编码,并针对码值做十六进制编码,所以使用escape针对汉字编码会得到形 如”\uxxxx”的结果;encodeURI/decodeURI,encodeURIComponent/decodeURIComponent函数 针对宽字节编码却不同于escape,首先针对宽字节字符进行UTF-8编码,然后针对编码后的结果进行“%”替换,得到结果。以上所述都是针对宽字节字 符而言,对于编码靠前的ASCII字符而言,上述三组函数的安全字符的范围也有所不同,具体可在上文中了解。

base64编码

base64编码在前端通常用于图片和icon的编码,它将每3个8位字节为一组,分成4组6位字节,并且每个字节的高位补零,形成4个8位的字 节,由此可看出base64编码是可逆推的。在大多数浏览器中,提供了ASCII字符的base64编码函数,即window.btoa()。该函数无法 针对宽字节进行base64编码,若针对中文编码,则需现转换位UTF-8编码,然后进行base64编码。

 
 
  1. function unicodeToBase64(s){ return window.btoa(unescape(encodeURIComponent(s))) } 

通过encodeURIComponent对宽字节字符编码,是“%xx”形式的编码,与UTF8编码的区别仅在于前缀(这是由规范RFC3986决定的,将非ASC字符进行某种形式编码,并转换为16进制,并在字节前加上“%”)。因此通过unescape(encodeURIComponent(s))可以转化为UTF8字节。当然,也可自己写一个转换函数,按照一定规则便行为UTF-8编码的字节,如下例:

 
 
  1. ``` 
  2. unescape(encodeURIComponent("中国")) //结果:"中国" 
  3. encodeURIComponent("中国") //结果:"%E4%B8%AD%E5%9B%BD" 
  4. console.log("\u00E4\u00B8\u00AD\u00E5\u009B\u00BD") // 结果: "中国" 
  5. ``` 

通过简单的replace函数,就可以完成URL编码到UTF8编码的转换,进而完成宽字节字符到base64编码的转换。有了这个函数,我们手动生成一些data URI形式的内容,只需制定MIME类型和编码方式,就可以实现文本的转换,如以下代码:

```

 
 
  1. abc 
  2. // 未编码前:test 
  3. ``` 

前端UTF8编码与后端GBK编码的兼容

目前前端大都采用UTF8进行编码,不管是html、js抑或是css,而后端则由于历史原因大都采用GBK或GB2312进行解码,因此前端通过 parameter传递的URL编码的字符串就不可能直接在后台进行解码,为了更好的兼容性,前端可进行两次URL编码,即 encodeURIComponent(encodeURIComponent(“中国”)),这样后端接收到参数后,先使用GBK或GB2312解码, 得到了UTF8编码后再使用UTF8解码即可。两次编码主要是利用“ASC字符使用GBK或GB2312编码不变”的特点完成,富有技巧。

HTML实体编码与进制编码

实体编码针对HTML的预留字符而言,如“<>”等。实体编码有两种形式&实体名;&entity_number;,由于浏览器对&实体名;的兼容性有差别,因此***采用实体号的形式编码。

进制编码,顾名思义将ASC字符对应的码值按照十六进制或十进制编码,并转化为&#x;(16进制)&#D;(10进制)形式。

单单针对实体编码而言并没有什么特殊强调的点,之所以把它单独列为一个章节,意在强调这两种编码与js代码的作用域的关系。

 
 
  1. ')">cccc
 
  • cccc
  •  
  •  
  •  
  •  
  • 代码中列举了8个例子,***个在事件处理函数onclick中输出HTML片段;第二个则输出经实体编码后的HTML片段;第三个则是直接针对做16进制编码;第四个则是针对onerror事件处理函数做16进制编码;第五个则是在脚本中输出实体编码的字符;第六个针对事件处理函数做16进制编码;第七个则针对所有的字符做16进制编码;第八个则是在script中直接输出的unicode编码。

    对比结果,前两个例子在点击后都会弹出alert;第三个例子则在页面中显示文本; 第四个例子则会在页面加载初期弹出alert;第五、七会输出字符串;第六、八则会在第四个例子中的alert之后也弹出alert。现在分析这些结果, 通过***二个例子可知道,HTML标签中(除script标签)的内联js代码可以进行HTML实体编码,这是非常重要的一点,我们可以更为明确的进行验 证:

     
     
    1. cccc
     

    输出的结果自然是,这的确论证了我们上文提到的这一点;第三个例子说明了HTML解析器在进行词法分析前,首先进行解码,十六进制和十进制皆可,因此,结果自然输出形如的 字符串;第四个例子则紧接着论证了内联在HTML的并采用十六进制编码的js代码同样会被正确解析并执行,这说明了进制编码同样可被HTML解析器解析; 第五、七个例子说明在js中同样可以使用实体编码和进制编码,解析的结果会渲染在页面上;第六个例子则论证了上一观点,只针对事件处理函数做进制编码,执 行后页面弹出alert;第八个例子则是在js中执行unicode编码的字符串,正常alert。

    由此可见,js代码内联在HTML的非script标签内,则会遵守HTML编码规范:进制编码和实体编码;而在js代码(script标签内以及js文件内)中,则遵从js编码:1,unicode形式编码(\uxxxx)2,普通的16进制编码(\xH),这可通过第八个例子得到证明。之所以在本节提到这么多编码特点,主要提醒大家在预防XSS时需要注意的几点:

    JS编码

    其实在上节中已提到了js编码,即js可执行unicode编码和十六(八)进制编码后的字符串,但是不支持十进制编码的字串。具体操作可通过常用 的几个函数来实现,如“eval,write,setTimeout,Function”执行编码后的字符串;同样,对于十进制编码的字串,通过结合 String.fromCharCode和eval同样可以执行。

    在此附上笔者实现的字符转换,更为灵活的实现各种自定义形式的字串编码:

     
     
    1. var Code = {}; 
    2. /** 
    3. * @param str 待编码字串 
    4. * @param jinzhi 进制编码 
    5. * @param prefix 前缀 
    6. * @param postfix 后缀 
    7. * @param count 总共编码的位数,默认为4 
    8. * @returns {string} 
    9. */ 
    10. Code.encode = function({str = '',jinzhi = '16',prefix = '\\u',postfix = ';',count = '4'} = {}){ 
    11.     var ret = ''; 
    12.     var addZero,tmp; 
    13.     for(let i=0;i
    14. tmp = str.charCodeAt(i).toString(jinzhi); 
    15. addZero = count - tmp.length + 1; 
    16. ret += prefix + new Array(addZero).join('0') + tmp + postfix; 
    17.     } 
    18.     return ret; 
    19. }; 
    20. Code.decode = function({str = '',jinzhi = '16',prefix = '\\u',postfix = ';'} = {}){ 
    21.     var ret = ''; 
    22.     var splits = str.split(';'); 
    23.     for(let i=0;i
    24. let tmp = splits[i].replace(prefix,''); 
    25. ret += String.fromCharCode(parseInt(tmp,jinzhi)); 
    26.     } 
    27.     return ret; 
    28. }; 
    29.  
    30. console.log(Code.encode({str: ''})); 
    31. console.log(Code.decode({str: Code.encode({str: ''})})) 

    另外,对于js输出点的过滤其实并不仅限于上文提到的如eval、setTimeout、Function等几个,由于JS语法比较灵活相对“漏洞”较多,可使用的“线索”也越丰富,如前段时间在Stackoverflow上发现的一个问题,即

     
     
    1. (0)['constructor']['constructor']('return "abc;"')() 

    同样可以执行JS代码,确实挺有特点的,具体为什么上述形式可以执行代码,请读者自己仔细品味。

    新闻标题:前端开发中的字符编码详解
    网页路径:http://www.shufengxianlan.com/qtweb/news45/156145.html

    网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

    广告

    声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

    猜你还喜欢下面的内容

    解决方案知识

    分类信息网