最近在学习一些xss有关的知识,碰到了很多精彩的文章,总结了一下其中有关于编码方面的知识,分享一下,可能不够完善仅供参考。前三点都是基础介绍,可以绕过。

一、url编码:
    1.介绍:
    一方面,url的设计者,考虑到安全传输问题,防止url字符丢失,所以选用了相对较小的、通用的安全字母表。另一方面,url的设计者希望url是完整的,有时候需要url中包含除去通用安全字母表之外的二进制数据和字符(比如中文)。所以url引入了一种转义机制,将不安全的字符编码为安全字符再进行传输。
RFC文档规定了一些字符,这些字符包括:
    非保留字符集:英文字母(a-zA-Z)、数字(0-9)、-_.~4个特殊字符。
    保留字符集:在url中有些字符被保留起来,有着特殊的意义,比如“/”作为路径组件中分隔路径段的定界符。RFC3986中指定的保留字符:! * ' ( ) ; : @ & = + $ , / ? # [ ]
    不安全字符:还有一些字符,当他们直接放在Url中的时候,可能会引起解析程序的歧义。这些字符被视为不安全字符。如:空格'<> #% {}|\^[]`~
    只有非保留字符和位于正确功能位置上的保留字符才允许出现在URL里。其他字符,如不安全字符和不在正确功能位置上的保留字符(例如在查询组件中出现的/)等,需要出现在url里时都要经过编码才可以。此外还有一些受限字符(ASCII字符集的不可打印区间0x00-0x1F,0x7F以及大于0x7F的字符),也需要进行编码。
    2.编码方式:
    百分号编码:url编码包含一个百分号(%),后面跟着两个表示字符ASCII码的十六进制数。例如:空格转为“%20”。非ASCII字符(如utf-8),需要使用ASCII字符集的超集进行编码得到相应的字节,然后对每个字节执行百分号编码。
二、html编码:
    1.介绍:
     url编码类似,一些保留字符出现在文本节点和标签值里是不安全的。比如“<>”会导致浏览器误认为标签。如果想要正确的显示这些字符,需要使用html编码。
     2.编码方式:
     实体编码:一般以“&”开头,“;”结尾,可以不加“;”。如:“<”转为“<
     进制编码:以“&#”开头,加上字符的数值,“;”结尾,可以不加“;”。字符的数值可以是任意十进制ascii码或Unicode字符编码。十六进制的数值需要在编码数字前加“x”。如:“<”转为十进制的“<”或十六进制的“<”。
三、javascript编码:
    1.介绍:
   在一些硬件里,无法显示或输入Unicode字符全集。为了支持这些硬件,javascript定义了一种特殊序列,使用6ascii字符来代表任意16Unicode内码。
    2.编码方式:
    数字形式:\u后面加4位16进制数字(或\x后加2位16进制数字),按字符的uncode数值编码,不足位数以零填充。如:“<”转为“\u003c”或“\x3c”。其中“\u”开头的Unicode转义方式可以用在字符串之外的位置,其他的不可以。
    反斜杠转义:在一些特殊字符前面加反斜杠。如:“<”转为“\<”
四、自解码机制和解码:
    从以上介绍可以看出,编码其中的一个作用就是防止用户或程序员使用一些保留字符时破坏掉当前语言本身的结构,这样一方面不利于用户的体验,另一方面会被一些用户利用,从而把数据当成代码执行。从这个角度来看,编码是可以防止用户提交的数据被当成代码执行的。我们想要通过编码进行绕过拦截程序,最终被当成代码执行,也就只能利用一些自解码机制或者程序员的解码去进行突破。
    1.服务器端自解码
    在《http权威指南》中2.4.4节中讲解url编码时有一段话:
    “有时,有些人会恶意的对额外的字符进行编码,以绕过那些对URL进行模式匹配的应用程序——比如,web过滤程序。对安全的URL组件进行编码会使模式匹配程序无法识别出它们所要搜寻的模式。总之,解释URL的应用程序必须在处理URL之前对其进行解码。”
    例如:在url地址栏中输入“%27qwe”后,服务端代码:
  1. <?php print_f($_GET[“name”]); >

复制代码

    在页面中输出的是“’qwe”,这是因为php中$_GET获取的是url解码后的内容,所以这一步是被php解码($_SERVER[‘QUERY_STRING’]获取未解码的数据)。
    xss编码学习总结-开水网络

 

    所以推测,使用url编码可以绕过两种情形。一种是程序员使用的$_SERVER[‘QUERY_STRING’]接受的数据,对其进行过滤,之后再解码。一种就是服务端使用的$_GET$_POST接受数据,但是碰到apache mod_security、安全狗、waf之类的在传入php处理之前的程序时,他们对数据包中的特殊字符进行过滤和拦截时,我们可以通过url编码的方式进行绕过。
   其实浏览器对用户提交的utl中一些数据也会进行默认的urlencode行为。如:firefox会对“’”`<>”编码,chrome会对“”<>”编码。
    2.浏览器的自解码
    在javascript中使用document.write(或innerHTML)向树构造器中添加标记或数据时,js引擎读取代码时会把其中的js转义去掉。如:
  1. document.write("\<img src\=@ onerror=alert\(123\) \/\>")

复制代码

    这样的代码,会正常弹出一个alert的框,不会受到“\”的影响。“\u”这类的编码是可以用在字符串之外的位置,如:\u0061lert("123")可以弹出,但是这种情况不能用来替换圆括号或引号。
    在html标签中的事件属性,比如onerror等支持js伪协议,可以执行js代码,如果是碰到html的实体编码,当数据交给js引擎之前,会进行html的去转义。比如:

xss编码学习总结-开水网络

 

 

    去转义后为<img  src=@  onerror="javascript:alert('1');">也会弹出一个alert的框。
    3.xss中的编码绕过
    从上面两种可以看出,js引擎或html的树构造器在解析代码时会把代码中的转义去掉,这样假设我们使用js编码(转义)数据,当js引擎通过document等交给html时代码中的js转义已经被去掉了。如果过滤xss的代码没有考虑到这个层面的话我们就可以进行绕过了。
    需要注意的是<script></script>中的数据是直接交给js引擎处理的。所以script标签中的代码不会进行html的去转义。而当他们发生交互时,这种自解码机制导致的问题就会出现。看过jiayzhan的《XSS解决方案系列》的就会知道,这就是所谓的二维解码。一维解码也就是,js与html不交互时,解析代码时也会去转义,我上面也说过,编码可以防止破坏掉当前语言本身的结构,一维编码本身就是防御xss的手段。js编码中的“\u”类型的还是可以绕过关键字检测的。
    三维编码也就很好理解了,如果服务端代码“document.write(<?phpprint_f($_GET[“name”]); >)”,用户提交数据后会先url解码($_GET),然后js解码(script中的代码会交给js引擎),最后html解码,也就是三维了。三位编码的形式可以是多种多样的,所以绕过的可能性也就会比较大。
绕过一般也就发生在二三维编码上,防御xss的话从编码入手也可以,有几个维度的解码,就倒着做几个维度的解码即可。用好编码与解码,也是安全上犀利的攻防战。

 

如果想要更详细的了解这方面的知识,还是推荐大家也去读一下以下文章。
    《web之困》第二章、第四章和第六章。
    《web前端 黑客技术解密》第六章
    Freebuf上jiayzhan的《XSS解决方案系列
    http权威指南和js权威指南上也有讲,篇幅不是很多。

 

工具和资源
    Fiddler 查看浏览器提交的数据很方便。Chrome的F12也是很强大。