—《The Tangled Web: A Guide to Securing Modern Web Applications》

这本书看了之后觉得似乎只是晃过一眼,虽然里边讲的林林总总的东西,之前都有接触和学习过,但是作者总是会点到一些这些东西的核心问题。这是一本很牛X的书,二百多页,能读出来八百页的感觉。作者也显示出了超级深的功底,web安全方向,涉及的内容非常宽泛,而作者反而还能够处处设计,且没什么废话,处处点到核心。所以,细读这本书,我是想找到作者每一个提到的部分的微言大义,将其收获扩展到最大。

这也是一本疯狂的吐槽书,在作者的眼里,整个Web 应用界,混乱不堪,看似繁荣却处处都是破绽,而且确实如此,因为作者随随便便便发现了数百个安全漏洞。

所以我以逐章节的形式,写自己的读书笔记,希望在阅读和反刍的过程中,收获到更多的信息,构建起更完善的知识体系。

这里放一个前言

Web安全为什么存在,是因为它沉寂的太久,而又崛起的太快,崛起的过程中,各方势力互相角力,争议不断,浏览器的厂家拥有强势的决定权,W3C 理事会又缺乏足够的控制能力,组织内部又常常意见不统一,造成了Web 一路发展过来,有许多千奇百怪的新东西出现又消失。再加上天然的Web 的强交互本质,其在安全上存在的风险是巨大的。

首先,用户是Web 应用的使用者,而大部分缺乏安全素养的个人用户,在使用Web应用的时候,怎么可能会去关注那些林林总总的安全风险问题,所以,首先web应用不应当寄希望于将安全的决策权交给用户,然而这一点上浏览器上做的并不好,虽然在逐渐改进,但是有很多忽然跳出来的选项,跳出来的专业词汇,对于小白用户来说就是一个灾难,大多数情况,小白一般就会忽略这些安全风险提示,直接操作到最后。

其次,Web 运行环境难以隔离,各类相关数据杂糅在一起难解难分,在其他领域,数据对象,用户层应用,操作系统内核之间界限清晰,每个应用程序的进程各自独立,相互通信,输入输出都要靠系统内核进行调配。然而在浏览器中,却不存在这样的隔离,文档和代码交融在一起,无关的应用之间无法完全隔离,因为他们常常使用了相同的全局JavaScript运行环境 ,这也就造成了诸如跨站,CSRF等等的攻击。

同时还有一些不言而明的缺陷,比如缺乏一个统一的安全体系,在浏览器领域,同源策略算是他的一个核心安全范式了,但是这种同源策略仍然是问题多多的,比如复杂的使用场景(JavaScript DOM访问, XML HttpRequest API,HTTP Cookie, 本地存储API, FLash,java插件等等等)。比如跨浏览器交互的问题,比如客户端和服务端的区别逐渐模糊,会在如今安全仍然非常脆弱的情况下,增加复杂度,也使得设计层面更加混乱,进一步增加了风险。

URL 开始

我们都知道一条URL 对应的是Web 服务器上某个独一无二的资源,在简短的一行长度里,我们希望浏览器和服务器能够获取足够的信息量,RFC 文档为URL 制定了一系列规则,好让一个URL 里可以携带更多信息,所以五花八门的功能和内容也会爬上URL, 而此时由于繁多的URL 花样加上各种浏览器的不同解读方式,也让其漏洞百出,有无数安全的隐患。

为了让URL 拥有更多功能,于是有了伪URL ,它不是资源,而是包括了一些功能,诸如脚本引擎,集中特别的文档渲染模式等功能。但是由于URL 本身机制上的疏漏和浏览器对协议的解析机制的不同,带来了诸多安全问题。具体RFC 文档可参见,RFC3986,以及RFC 1738,2616等次要文档。

URL 结构

在之前的笔记里,已经整理HTTP 里的大部分内容,其中就包括URL 部分,所以一个典型的结构是:
://:@:/;?#

协议名称

这是第一个会出问题的地方,IANA 维护了官方认可的有效URL 地址,如http,https, ftp, 等几十项,而实际上浏览器会支持一些额外的协议,如data: , javascript: 这些都会带来安全问题。

(此处应插入更详细解释)

在RFC 1738 定义中,要求绝对URL 在冒号之前只能出现字母,数字,「+」 ,「-」 , 「.」。但是在实际上各个浏览器都有一些奇怪的支持和不支持方式,在书中列举的例子,我偷懒复制一下别人写的思维导图:

实际测试下,我手动构造一个HTTP 请求,在协议头这个部分,他也会忽略掉空格0x00这样的字段,比如ht%20tps://www.baidu.com/index.html 仍然是能够正常解析的,我想这应该是服务器方面解析的原因了,目前不知道这上边编码的混乱如何利用制造安全问题。

层级URL 标记符号

仍然是我们的RFC 1738 要求我们的绝对URL 应当包含’//‘,如果没有这个东西,后边的URL 就无法正常解析了,只能把他们看成一个含糊的与某个协议有关的内容了。

然而文档里包含了很多含混不清的东西,比如如果一个费层级结构的URL 带有了// ,如何处理,同时如果一个协议没有带//该如何解析,这些在1738 文档里都没有讲清楚。最常见的就是http:www.google.com这样的结构,或者是javascript://www.google.com/%0Aalert(1)。不同的浏览器都有各种各样的解释方式。

访问资源的身份验证

这一块貌似是没什么重点内容了,直接挂别人的思维导图,略过:

服务器地址

这里就是放域名或者IP地址的地方,IP还有IPv4 的,或者是放在方括号里的IPv6。RFC 要求规范的IP地址写法,然而大多数应用都支持八进制,十进制,十六进制的写法,甚至还可以把其中几个或者全部8位元数据拼在一起转成单个整数的写法。所以实际上一下几个地址是一样的:

大部分浏览器都会忽略出现在URL 内且数值范围在 0x0a~0x0d 和 0xa0~0xad之间的控制字符。因为这些是换行符等等特殊字符。

服务器端口

这里可以指定端口号,作者说这里会造成一个安全问题,就是我们可以用浏览器向人以网络服务发送攻击者提供的数据,尽管浏览器并不支持这些服务的协议。作者举得例子虽然知道是什么,但实在是不知道这怎样引发安全问题,作者说后边会详细说,然而宝宝粗略的翻阅后边,没发现哪里有讲,等我找到了,我再回来补上。

层级的文件路径

这一块不用做过多解释,他是直接从UNIX 目录语义拿过来的,所以也会出现/../这种路径,作者没有提其中的安全问题,但这一块大约是安全问题比较多的,通过/../可以向上搜索文件,如果服务器没有适当的设置权限,或者是对此类的请求进行过滤的话,就有可能引起文件泄露。

查询字符串

用于把一串非层级格式的任意参数传递给由前边路径对应的资源。关于这一块的格式,RFC 文档里都没有任何强制性的规定,所以作为浏览器,或者是服务器,它可以以任意喜欢的方式去处理和解析。这也是在URL 上存在注入的原因之一,通过在URL 里嵌入脚本,企图注入路径上的文件,达到攻入的效果。

保留字符,百分号编码

为了不破坏URL 语法,一些符号需要保留:: / ? # [] @
同时RFC 也规定了若干底层分隔符,这些符号留给上层协议或者具体的应用去实现:! $ & ‘ () * + , ; =

以上这些都是原则性的,但是仍然允许应用有合适的使用方法,那就是对这些字符进行编码,也就是我们常说的百分号编码或URL编码,以一个百分号加上字符的ASCII编码对应的2位16进制数字替换这些字符。比如/ 编码成%2F,百分号本身也会被编码成%25,查阅wiki, 列表如下:

值得注意的是,中间程序在处理URL 的时候,不应当对这些URL 保留字符进行编码,以免传递下去的时候改变了URL 的含义。

另外,虽然禁止,但如果接收到这样的字符,浏览器方为了保证高容错率,一般都会有一套处理方式,但是RFC文档中没有明确说明,所以各家浏览器处理起来完全自由发挥。

同时,RFC 还规定了一批非保留字符(这个听起来有点莫名其妙了),包括字母,数字,- . _ ~ ,意思大概是说,只有这里边的字符,以及出现在正确位置的保留字符,才能够使规范的URL。但是规定里并没有说不可以对非保留字符编码,出现在URL。所以对这些保留字符进行编码,其表示的效果仍然是一样的,例子如下:

对此可以在浏览器中进行尝试,完全一样的效果。当然这种编码也是因为攻击的一种方式。而一般的,浏览器会在地址栏直接进行强制解码,让这些非必要的编码字符以规范化形式展现。

同时,还有一些,既不在保留字符,又不在非保留字符,RFC 文档的描述有一定的缺失,此时的处理就要看浏览器了,这里直接上图:

另外,在非正常的US-ASCII 编码问题上,浏览器的解决之道各种混乱,在家这各种编码规范鱼龙混杂,这里我还没有完全搞清楚,此处留坑。

常见 URL 协议及其功能

这里讲讲协议的两三事:

浏览器本身支持的,与获取文档有关的协议

这就是最基本的浏览器内部直接处理,通过特定的传输协议,获取指定文档内容,通过常规的内核解析引擎的逻辑处理。最常规的协议,http,https,ftp等。

第三方应用和插件支持的协议

当浏览器匹配到这个协议的时候,会将处理转交给相应的第三方应用或者插件上,比如arobat:,callto: ,等一票调用第三方应用的协议,大多数情况来说,这些协议对于发起他们的Web 应用本身没有安全性的影响,但是那些第三方协议的处理程序往往是漏洞百出,并有可能导致操作系统被入侵。

未封装的伪协议

这些伪协议,是为了访问浏览器脚本解析引擎和某些内部功能的,不需要从远程获取数据,同时一般也无法通过互联网访问到。如最常见的 javascript:,data:(不需要额外的网络请求,创建一个短小的内置式文档),这两个都是高危险的协议。

封装过的伪协议

这类特别的伪协议,可以放在任何的URL 前面,只是将取回的内容强制进行特殊的解码或者渲染显示。比如最常见的查看源码,view-source: 同时其他浏览器还有一些各自支持的伪协议:

在这种伪协议的方式下,会隐藏最后实际有浏览器处理的真是URL ,比如 view-source:javacript: 后边跟恶意代码,就会达到攻击的目的。或者说,微软的mhtml 协议,会护绿服务器返回的HTTP 内容设置指令。

最后要说的是,这些协议里暗藏各种杀机,而简单的设置黑名单又很难完全杜绝,因为攻击者可以对其进行各种各样的变形,如加上制表符,将JavaScript 编程 VBScript等等,都同样会触发安全问题。

相对URL 解析

RFC里定义了,URL 字符串不是以有效协议名开始,后边没有跟冒号,或没有// ,都是相对URL。按道理讲,这讲的很明确了吧,如果有上下文环境,那就放在上下文环境里构造成安全的相对链接,如果没有正确的上下文环境,那就拒绝访问。

然而,事实上并没有看起来那么轻巧,各种浏览器的具体实现千差万别,有效协议名称的字符集又各不相同,以及各种替代// 的做法出现,这时候,在处理上就应当小心翼翼,因为随时可能会有黑客构造出一个URL 的变形,对你的文件资源进行攻击。

下面有一些情况:

有协议名称,没有授权信息

如: http:foo.txt 这是RFC 文档早期的疏漏,规范认为这种URL是一个无效的绝对地址,但是,提供的解析算法又会对这种地址有一些含混的解释。在现在的地址解析算法中,这种形式的URL ,协议,路径,查询字符串或者片段ID以URL 为准,授权信息以引用页面为准。这个可以在HTTP 构造包时候进行尝试。

没有协议名,但有授权信息

如: //example.com 这也是个奇怪的写法。此时的处理方式一般是,保留当前页面的协议,字符串作为新的授权信息字段。这个好理解。

没有协议名,没有授权信息,有路径

如: ../notes.txt 这里要用到一些常见的处理方式了,如果开头没有/ ,要添加到引用URL 最右边的的/ 后边,而如果初始路径形如 http://example.com/files/index.html ,则需要将其部分砍掉,拼接路径。

只有片段ID

如 :#bunnies 这种情况,除ID 外,全部信息原封不动引用,然后替换片段ID 部分。通常这样的 构造,不会让页面重新加载。

安全工程指导

如果要构建用户用户输入的全新的URL 时候,一定要慎重:

如果允许用户构造URL 的路径、查询或者片段ID 部分,应当注意,如果其中一部分不能正确的的转移,就有可能会产生意外的结果。所以,最好的做法是,如果要插入攻击者能控制的字段值,应当对除了字母和数字,都进行百分号编码的转义。当然,这种方法也只是部分的解决了问题,还有一些二次转义等绕过的问题。

如果允许用户提供协议名称或者是授权信息部分数据:这里会造成严重的代码注入问题,这时候就应当设置合理的URL 过滤器了。

一些常见的URL 过滤方式:

而如果需要从接受的URL 那里进行参数解码,注意,任何从URL 里获取到的值,准备放入数据库查询前,或者是拼接成新的URL前,一定要做好过滤,这些值不一定是安全的,随时可能造成安全问题。


至此,URL 部分写完了,但是中间还是留存有许多坑,后边还需要在这些坑上做修补,对细节更加理解。URL 存在的安全问题,应该是较为显眼的攻击了,几年前也许这些攻击有如家常便饭,现在在安全编码的规范下,很多人都会更加关注URL 上的问题,所以在URL 上做文章的几率也在逐渐变低。

所以,联系到我之前做数据挖掘方向上的网络安全分析,如果单纯的拿到服务器的日志,(对于服务器的日志格式,《HTTP权威指南》中有一定介绍,各家服务器也支持自定义)拿到默认的数据格式的数据的话,可以构造的特征值,除了时间分析,频率分析,大概也只有URL 了。虽然URL 藏有非常多的玄机,但在处理上千变万化以及莫名其妙,直接处理都有难度的情况下,还试图通过挖掘的形式直接找到玄机,可能还是不够的。所以,为了能够抓取到最够有用的信息,我们还需要在服务器上获取更多的信息,如对系统调用的日志,如每次发包的的协议头部分内容,甚至还有可能要分析实体部分。当然,目前有一个清晰地轮廓,但是内里确是模糊的,在继续看下去的过程中,应该会有更多的收获。

script>