Web之困笔记

Posted by Lnyas on

前段时间orange的BH2017议题中提到的不同语言和库的url解析对SSRF的影响,让我想起了web之困中曾经讨论过浏览器对url的解析过程也同样是一团乱麻,之前只是快速过了一遍这本书,最近有时间了再重新看一遍。

ff = firefox; ch = chrome; sf = safari

一切从url开始

URL结构:scheme://login:password@address:ort/path/to/resource?querystring#fragment

协议(scheme)

:结尾,规定在:之前,只能出现+ - .三个符号,但在实际环境里所有的浏览器都会忽略换行符和空格,IE还会允许所有的不可打印字符,chrome会忽略0x00和NUL。

层级url

含有//的url

问题:如果碰到一个非层级的url含有//如何处理?反之亦然

导致各家浏览器的解析不同:

  1. http:example.com 在ff,ch,sf里会等同于http://*
  2. javascript://example.com/%0Aalert(1) 所有常用浏览器会认为是非层级的javascript伪协议

身份验证

不提供则默认匿名,大多数浏览器对身份验证部分接受所有字符,而sf拒接< > { },ff拒绝换行符

服务器地址

必须提供:不区分大小写的域名、ipv4地址或在[]中的ipv6地址,ff还接受在[]中的IPv4和主机。ip地址可以使用八进制、十进制、十六进制,甚至可以吧其中几个或全部8位元数据拼在一起再转成单个整数的写法:127.0.0.1 -> 0x7f.1

层级的文件路径

/aaa/bbb/ccc.txt 这个格式是直接从UNIX目录语义借用过来的,所以也支持在路径中出现/../和/./

如何解析一个url

  1. 提取协议名称:扫描第一个:字符,左边部分为协议名称
  2. 去除层级url标记//
  3. 获取授权部分信息:扫描/ ? #,首先定位登录信息(@),接着提取目标地址,大多数浏览器接受\作为分隔符的方法
  4. 确定路径
  5. 提取查询字符串
  6. 提取片段id

实际上url的解析非常困难和混乱:

一个例子:http://example.com&xxx=123@167772161/这个url的目标地址实际是10.0.0.1(167772161转化为十六进制为A000001,即10.0.0.1)

又一个例子:http://a.com\@b.cx 在ff里,会带往b.cx,而在其他浏览器,\会被认为是分隔符,会带往a.com

IE里一个更抓狂的例子:http://a.com/;.b.cx ie允许;出现在主机名称,其他浏览器会补全为/a.com/;.b.cx

保留字符和百分号编码

url解析的大前提:: / ? # [] @ ! $ & ‘ () * + , ; = 不出现在url里,否则会破坏解析。但是有时需要使用它们,就需要进行url编码,这个编码由浏览器完成,但是还是会出现分歧:

例如http://a@b@c 应该对哪个@进行url编码?ie和sf认为编码后一个,其他的编码前一个。

注意:禁止保留字符以非编码形式出现,但是不禁止非保留字符以url编码形式出现,所有浏览器都不会编码片段id

对非us-ascii文本字符的处理

IDNA标准,2002年被发现漏洞,前段时间无法分辨的钓鱼网站域名也是类似的方法?

常见的url协议及功能

浏览器本身支持的协议

由浏览器内部直接处理 http: https: ftp: file: ff仍然支持gopher: ie仍然支持shttp:

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

根据协议交给外部的应用程序 mialto: news: firefoxurl: (在浏览器里启动ff程序)cf: (在ie里调用chrome) 第三方协议的处理程序往往漏洞百出,尽量少用

未封装的伪协议

javascript: data:

封装过的伪协议

view-source: jar:

相对url的解析

如果url不是由有效协议开始,并且没有冒号或者//,那就是需要被引用的相对url,如果解析这个url的时候没有上下文,就拒绝访问。

相对url的类型:

  1. 有协议名称,没有授权信息(http:foo.txt),以url自身为准,出了授权信息以引用页面为准。
  2. //a.com: 保持当前页面的协议不变
  3. ../1.txt: 拼接在url最右边的/后面
  4. ?a=b: 原封不动的加上查询字符串和片段id
  5. #url: 同上,不会导致页面重新加载

HTTP协议

HTTP/1.1 要求客户端不仅要接受CRLF和LF两种换行符,还要接受CR字符

对重复头域的解析

没有规定接收第一个或第二个,一半的浏览器接收第一个,而另一半接收第二个,而现在服务端,apche接收第一个host头,IIS不接收多个HOST头的情况

也没有规定两个版本不同(HTTP/1.0 HTTP/1.1)但是一样功能的头域同时出现会怎么样

HTML语言

传统HTML解析不严格,相反XML解析非常严格:所有标签必须严格匹配,命名大小写要一致,要显示明确的闭合

HTML解析器

用XHTML解析器,对格式错误的容忍度极低,而用传统HTML解析器,则会出现许多含混的解析 <img src="xx" title="hello" class=example>

img和src中的空格可以用不太常见的垂直制表符(0x0b)和进纸换页符(0x0c)替代,ff里可以用正斜杠代替,opera里可以用0xa0代替。

ie可以用`代替引号和双引号

ie会识别<img src=1.jpg?value=">123!">中的value为”>123!”

多重标签的交互

<i <b>

大多数浏览器会解析为<i>,但在ff4.0之前会解析为<i><b>

层叠样式表

在HTML里嵌入样式表时,会优先执行对HTML的解析,且HTML的解析是独立于CSS语法规则之外的,所以在CSS中如果包含HTML语法会非常不安全。

CSS的解析在遇到语法错误的时候仍会继续工作

CSS用\后面跟1-6位的十六进制数字来转义,例如e可以编码为\65 \065 \000065,但如果e后面一个字符是有效的十六进制数字的时候会出错,例如ea被编码为\65a,则会按照\65a整个来解析,成为unicode里一个阿拉伯符号。CSS针对这个问题有笨拙的解决方案:转以后加一个空格作为终止符。

浏览器端脚本

JS的解析和执行阶段被严格分开,所以允许在一个script标签内先调用函数再定义函数(只对函数有效而对变量无效)。

执行顺序和错误处理:同一个标签内解析正确后按顺序执行,如执行遇到错误不执行错误后面的语句,标签之间的错误互不干扰。

JS脚本字符编码

  1. 对某些控制字符,可以用c风格的方法:\b \t \v \r \n
  2. 3位数字按8位原字节八进制字符编码的方式,无前缀(\145代表e)
  3. 2位数字,按8位原字节16禁止字符编码,前缀位x(\x65代表e)
  4. 4位数字,按16位原字节的16禁止Unicode数值编码,前缀为u(\u0065代表e)
  5. 反斜杠后面跟非8进制数字,也不是1中的转义字符,按反斜杠后面的字符的字面意义直接处理

浏览器安全部分-内容隔离

DOM的同源策略

协议-域名-端口

document.domainpostMessage()

同源策略与浏览器身份验证信息缺少同步机制 -> csrf等漏洞的产生

XMLHttpRequest的同源策略

CORS:返回的数据包里包含特定响应头的时候,浏览器才能读取响应数据

源的继承

about:blank data: 和 javascript:

同源策略之外的世界

点击劫持

X-Fram-Options: sameorigin

其他的安全边界

直接跳转到 file: 会造成的安全影响 DNS rebinding -> DNS pinning

内容识别机制