谈Web前端安全编码

jopen 9年前

 

最近开发中涉及到有关输出正确的HTML标签这样的问题,正好对字符编码这块儿多看看,之前对这个方面认识的不深,思考的确实不够,如果下次再碰见类似的问题,若再次不少时间去调研的花,就得不偿失了。

就像正则表达式一样,似乎你知道它,但是每一次开发都需要现取查、现取测试,估计你会像我一样,每一次都会花些意想不到的时间。

总之,重在学习。

有一篇文章,这个博友已经总结的非常到位了,引入于此,看到的同学,希望可以多看看他做的总结:

在web的开发的开发过程中,前端总是在处理后端打的各种变量,变量可以包含着中的各种各样的字符,如果不对这些字符进行”特殊“处理的 话,轻者导致页面不正常的显示,潜入了其他的东西,亦即页面挂了,或者弹出不应该弹出的东西,这些都是我们不期望看到的,重者可能导致密码泄露,网站的访 问量突然猛增,服务器挂掉。

在前端的开发中,涉及到以下几种语境:

1)直接显示在页面上, eg:<div>{%username%}</div>,<input type="text" value="{%username%}"/>

2)在script 标签中,eg :<script>var test = '{%username%}';var test="{%username%}"</script>

3)在页面事件中,eg:<div onclick="alert('{%username%}')">334455</div>

4)在innerHTML 的语境中,eg:<div id="test"></div> <script>var test="{%username%}";document.getElementById('test').innerHTML =test; </script>

5)在页面链接的url中:eg:<a href="{%username%}"></a>

6)提交url参数处理

7)js 获取url参数值的时候

下面来一个一个的分析上面提到了7种语境中的转义情况:

1)直接显示在页面上(简称页面html环境中):

为了保证用户的本意,完完全全的展示在页面上,这类主要是防止标签的自闭合,属性中的单引号,双引号已经存在的情况下不正确显示,所以必须转义4个字符:<,>,",'  to为转义的意思(下同)

(1)<  to &lt;

(2) > to &gt;

(3)" to &quot;

(4)' to &#39;

2)在script的标签中(简称js环境中):

在javascript 中 ” 和' 都是表示字符串,没有任何区别,所以如果变量中出现了这2个字符,就会影响后面不正确显示,所以必须转义这2个字符 ,同理,如果变量中包含\ 会将后面的'或者“给转义掉,

变成真正的’和”,也没有闭合,导致语法错误,所以这个字符也需要转义,另外在我们的注释中存在/*  */ 这种形式,如果在变量中出现了 */这种字符,就会将注释掉的部分代码给暴漏出来,所以也要转义/字符,

综上所述,在script标签中要转义的字符为:

(1)'   to \'

(2)"  to \"

(3)\  to \\

(4)/  to \/

3)在页面事件中:

这类语境涉及到了页面html和Js 环境,要执行什么转义呢?到底是先html 在js,还是先js 再html 转义呢?我们来看一个例子:

eg:<div onclick="alert('{%username%}')"></div>

当username = " 的时候,如果是先html ,然后再js 转义的时候,那么就是<div onlick="alert('&qout;')"></div>   我们拿到页面上去执行,发现语法报错

如果是先js,先后在html转义的时候,那么就是<div onlick="alert('\&qout;')"></div>   我们拿到页面上去执行,成功!!

所以结论是 先进行JS 转义,然后再进行html 转义,为什么是这样呢?因为这里它是要执行一个js函数的,如果都当做html来解析了,这里的js函数就不会执行,也就没有js 环境的意思了。

综上所述,在这累语境中需要转义的字符为:

(1)'   to \&#38;

(2)"  to \&quot;

(3)\  to \\

(4)/  to \/

(5)< to &lt;

(6)< to &gt;

4) 在innerHTML环境中:

这类语境首先是js环境中,其次是在html环境中,显然,先进行js转义,然后再进行html转义,需要转义的字符同上述3)

5)在页面链接的url 环境中:

这类比较复杂,url中本身涉及到很多的特殊字符,此外也会涉及到html 和js 环境中的赋值的情况, 特别注意,url 编码和html的编码是不一样的,见后文附录url编码表和html编码表

在html 和js环境中,需要转义的字符为:  ”  ,' ,<,>,\ ,/

在其他环境中,需要转义的字符为:+,空格,?,=,&,#, %

这类字符的转义如下:

(1)"  to %22;

(2)' to %22;

(3) <  to  %3C

(4) >  to %3E

(5) \  to %5C

(6) / to %2F

(7) +  to %2b

(8)空格 to  %20

(9)?  to  %3F

(10)=  to  %3D

(11)&  to %26

(12)#  to 23

(13) %  to %25

为什么要转义这些字符呢?稍微web开发的经验同学就知道,原因很简单,如果存在这些字符的话,不进行转义,那么我们就会得不到我们应该得到的东西

引申一点:在我们拼接url的时候,比如将表单中的数据提取出来,用ajax的方式提交的时候,也需要对上述字符进行转义,不然得到的也不是想到的东西

6)提交url 参数的处理:

1) Form  表单提交方式:不需要做任何处理,表单会依照页面的编码进行编码

2) ajax 的提交:因为ajax的提交的时候,是拼接成url的方式提交给后端的,所以必须要考虑对  +,空格,?,=,&,#, % 的转义,通常使用 encodeURIComponent进行转义

关于escape,encodeURI,encodeURIComponent  这三个函数的需要的转义字符,见后面的附件列表

7) js 获取url参数的值的时候

(1) 得到url中的参数值的时候,首先必须要进行unescape的转码才能使用,因为url中的一些特殊字符都经过了编码

(2) 将url的值设置到一些参数上时,比如隐藏表单上的value值的时候(作提交refer),需要进行escape 编码

附录:

1)html 编码: http://wenku.baidu.com/view/0dbaa1dc7f1922791688e8a2.html

2)url 编码: http://baike.baidu.com/view/204662.htm

3)escape,encodeURI,encodeURIComponent 的区别: http://www.alixixi.com/web/a/2008081147930.shtml

上述引文来自: web前端安全编码(模版篇)

我概要一下,分为两类,模板(或称为显示)相关,另一类是URL相关;

一、模板(显示)相关:

我们要把不确定性的后端文本变量(当然来自绝大部分来自用户的输入),为了让之正确的显示在页面中(也就是HTML中),或者正确的执行js代码,我们要对特殊字符进行转义:

HTML环境:

< 转义 &lt;  > 转义 &gt;  " 转义 "  ' 转义 '

JS环境:

' 转义 \'  " 转义 \"  \ 转义 \\  / 转义 \/

对应的工具函数如:

// 转义为HTML环境  // 主要用于innerHTML这种场景  var toSwitchForHtml = function(text){      if (typeof text !== 'string') {    throw new Error('The text must be a string !');      }      return text        .replace(/</g,'<')        .replace(/>/g,'>')        .replace(/"/g, """)        .replace(/'/g, "'");  };  // 转义为JS环境  // 主要用于执行js代码,如new Function(someStringFunctionFromServer);等  var toSwitchForJs = function(text) {      if (typeof text !== 'string') {    throw new Error('The text must be a string !');      }      return text        .replace(/\\/g,'\\')        .replace(/\//g,'\/')        .replace(/"/g, """)        .replace(/'/g, "'");  };

二、URL相关:

主要涉及3个可以对字符串编码的函数,分别是:escape,encodeURI,encodeURIComponent,相应3个解码函数:unescape,decodeURI,decodeURIComponent 。

相应的函数介绍,主要查看js api文档即可。

这里做了个统计表格

方法 编码范围 作用 备注
escape 不编码:
ASCII字母和数字、*+-./@_
其余全部编码为十六进制转义序列
在所有的计算机上读取该字符串。要注意,它是将需要转义的字符转义成UTF-16(因为JavaScript只支持16位UTF-16编码)码点。 关于这个转义之后的unicode编码方案,可参考: Unicode与JavaScript详解
encodeURI 不编码:
ASCII字母和数字、!’()*._~
URL特定标示符:;/?:@&=+$,#
其余全部编码,根据URL编码规则进行编码
对整个URL进行编码,而URL的特定标识符不会被转码。 url编码的含义:
url编码就是一个字符ascii码的十六进制。不过稍微有些变动,需要在前面加上“%”。比如“\”,它的ascii码是92,92的十六进制是5c, 所以“\”的url编码就是%5c。那么汉字的url编码呢?很简单,看例子:“胡”的ascii码是-17670,十六进制是BAFA,url编码是“ %BA%FA”。更多请参看: URL编码
encodeURIComponent 不编码:
ASCII字母和数字、!’()*._~
编码:
URL特定标示符:;/?:@&=+$,#
其余全部编码,根据URL编码规则进行编码
将文本字符串编码为一个统一资源标识符 (URI) 的一个有效组件。 请注意 encodeURIComponent() 函数 与 encodeURI() 函数的区别之处,前者假定它的参数是 URI 的一部分(比如协议、主机名、路径或查询字符串)。因此 encodeURIComponent() 函数将转义用于分隔 URI 各个部分的标点符号。

根据上面的表格,我们再来分析使用场景:

1. 当js使用数据时可以使用escape,比如获取或者设置URL中的参数值的时候(当然,约定该URL中传递的参数是经过unicode编码的)

2. 进行 url跳转 时可以整体使用encodeURI。如——

document.write(encodeURI("http://abc.com/do?name=文章&name=king"));

3. 传递参数 时需要使用encodeURIComponent,这样组合的url才不会被#等特殊字符截断。如——

<script >document.write('<a href="http://passport.baidu.com/?logout&aid=7&u='+encodeURIComponent("http://cang.baidu.com/bruce42")+'">退出</a>');</script>

弄清escape、encodeURI、encodeURIComponent的内容,量并不小,需要耐心去理解、去实践。

参考:

1. web前端安全编码(模版篇)

2. escape()、encodeURI()、encodeURIComponent()区别详解

3. URL编码

4. HTML转义字符

5. Unicode与JavaScript详解

6. escape,encodeURI,encodeURIComponent方法使用