您好,欢迎来到12图资源库!分享精神,快乐你我!我们只是素材的搬运工!!
  • 首 页
  • 当前位置:首页 > 开发 > WEB开发 >
    前端开发中的字符编码详解
    时间:2017-03-10 00:19 来源:网络整理 作者:12图资源库 浏览:收藏 挑错 推荐 打印

    前端开发过程中会接触各种各样的编码,比较常见的主要是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编码。

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

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

    ``` 

    unescape(encodeURIComponent("中国")) //结果:"中国" 

    encodeURIComponent("中国"//结果:"%E4%B8%AD%E5%9B%BD" 

    console.log("\u00E4\u00B8\u00AD\u00E5\u009B\u00BD"// 结果: "中国" 

    ``` 

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

    ```

    <a href="data:text/html;charset=utf-8;base64,PHNjcmlwdD5hbGVydCgxMik8L3NjcmlwdD4=" >abc</a> 

    // 未编码前:<a href="javascript: alert(1)">test</a> 

    ``` 

    前端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代码的作用域的关系。

    <div onclick="document.write('<img src=http://developer.51cto.com/art/201605/1 onerror=alert(23)>')">cccc</div> 

    <div onclick="document.write('&lt;img src=http://developer.51cto.com/art/201605/1 onerror=alert(23)&gt;')">cccc</div> 

    <img src=1 onerror=alert(23)> 

    <img src=1 onerror=alert(23)> 

    <script> 

       document.write('&lt;img src=http://developer.51cto.com/art/201605/1 onerror=alert(23)&gt;'); 

       document.write('<img src=http://developer.51cto.com/art/201605/1 onerror=alert(3)>'); 

       document.write('<img src=http://developer.51cto.com/art/201605/1 onerror=alert(23)>'

       document.write('\u003c\u0069\u006d\u0067\u0020\u0073\u0072\u0063\u003d 

    \u0031\u0020\u006f\u006e\u0065\u0072\u0072\u006f\u0072\u003d\u0061 

    \u006c\u0065\u0072\u0074\u0028\u0032\u0033\u0029\u003e') 

    </script> 

    (责任编辑:admin)