xss

xss知识点小记

Posted by kingkk on 2018-08-23

前言

最近在学习xss,记录一些比较重要的知识点。有时能从根本上解决一些问题的疑惑

同源策略

同源策略可以说是浏览器安全中最最基础也最为重要的部分了。同源策略限制了资源的任意加载,限制恶意请求。

何为同源

这个估计大家都比熟悉,简单的来说就是如下三点

  • 协议相同(http/https)
  • 端口相同
  • host相同

请求过程

跨域请求在html中的不同位置都会有发生,主要分为如下三类

Cross-origin embedding

嵌入资源,比如一些图片、视频、字体、css、js资源等

这种嵌入式的资源是可以跨域访问的

Cross-origin write

例如form表单的提交,以及一些link的重定向

这种情况下是不会受到同源策略的影响,表单数据,以及链接地址也是可以发往任何域的

Cross-origin read

例如利用ajax发送http请求,获取页面数据

这种情况下是严格受到同源策略的限制

需要强调的一点是,跨站请求的失败,并非是请求没有发起,而是请求已经发送到服务器,服务器也已经返回数据。只是在返回时被浏览器给拦截了

CORS

为了获取一些非同域的数据,于是有了CORS策略,来允许跨域资源的访问,对跨域的资源进行了一些限制

主要是通过一系列Access-Control-xxxx格式的http头来完成这一项任务

simple request

simple request主要是依据Access-Control-Allow-Origin 头来判别允许加载的源

例如当发起这样一段ajax请求时

1
2
3
4
5
6
7
8
9
10
xmlhttp=new XMLHttpRequest();
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.body.append(xmlhttp.response);
}
}
xmlhttp.open("GET","http://192.168.85.128");
xmlhttp.send();

由于对方服务器未设置相应的CORS头部,导致被同源策略阻拦,无法获取对应的数据

在远程服务器上利用php发送一段Access-Control-Allow-Origin头,让所有源的请求可以获得该响应数据

1
header('Access-Control-Allow-Origin:*');

当返回的响应带上这个头之后,浏览器就可获取到返回的信息

preflight request

不符合以下要求的,会触发一个preflight机制(搬运。。

  • GET 请求
  • HEAD 请求
  • Content-Type 为指定值的 POST 请求,包括text/plainmultipart/form-data以及application/x-www-form-urlencode
  • HTTP 首部字段不能包含下列以外的值:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width

该机制会在请求前发送一个OPTIONS检查,询问跨域相关信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-TEST, Content-Type


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-TEST, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

请求方发送了两个CORS头部

  • Access-Control-Request-Method: POST 询问是否可以使用POST传输
  • Access-Control-Request-Headers: X-TEST, Content-Type 询问是否允许自定义头部

服务器也响应了四个CORS头部

  • Access-Control-Allow-Origin: http://foo.example 允许该源请求数据
  • Access-Control-Allow-Methods: POST, GET, OPTIONS 允许的请求方式
  • Access-Control-Allow-Headers: X-TEST, Content-Type 允许的自定义头部
  • Access-Control-Max-Age: 86400 有效时间

浏览器解码

主要是为了解决为何有时候可以使用html绕过,以及一些标签优先级问题

HTML五类元素

空元素(Void elements)

<area>,<br>,<base>等等

不能容纳任何内容,因为不存在闭合标签,从而没有内容可以容纳至其中

原始文本元素(Raw text elements)

<script><style> 可以容纳文本

RCDATA元素(RCDATA elements)

<textarea><title> 可以容纳文本和字符引用

外部元素(Foreign elements)

例如MathML命名空间或者SVG命名空间的元素

可以容纳文本、字符引用、CDATA段、其他元素和注释

基本元素(Normal elements)

即除了以上4种元素以外的元素,可以容纳文本、字符引用、其他元素和注释

解析顺序

浏览器的解码顺序为

1
html解码 ==> javascript解码 ==> url解码

正是由于这个原因,导致在script标签中,只要遇到</script>标签就会进行闭合

在加载完html代码之后,浏览器会先对整个html代码进行一次解析,在解析中会进入几个不同的状态

  • <字符前 : Data state
  • <字符时 :Tag open state
  • 找到标签名: Tag name state
  • 属性名 :before attribute name state
  • 属性值 : Data state
  • >字符时 : 重新进入Date state

每遇到一个新的标签就会记录一个token,当一个标签完结的时候,就会释放掉那个token

在RCDATA元素标签中,会进入一种RCDATA状态,只认得</textarea><title>
从而在“<textarea>”和“<title>”的内容中不会创建标签,就不会有脚本能够执行。

svg外部标签,在解析它的时候,由于其支持xml协议,从而,svg还会对其内部数据进行一次xml解码,也就导致了svg标签内部可以使用html编码

因此HTML编码只有在如下几种情况下可以使用

  • Data state

    • 在标签外部

    • 在属性值时

  • svg标签内部

CSP(Content Security Policy)

简介

Content Security Policy (CSP)内容安全策略,是一个附加的安全层,有助于检测并缓解某些类型的攻击,包括跨站脚本(XSS)和数据注入攻击。

CSP的特点就是他是在浏览器层面做的防护,是和同源策略同一级别,除非浏览器本身出现漏洞,否则不可能从机制上绕过。

CSP只允许被认可的JS块、JS文件、CSS等解析,只允许向指定的域发起请求。

CSP不仅限制了js的引入,还限制了个各种静态文件资源的的引入。在默认情况下甚至限制了内联js的执行(就是在<script>标签中的js代码

CSP的从一定程度上缓解了XSS的危害,但由于其较强的限制性,网站要做较为详细的配置,若是配置不当,依旧容易引起XSS

启用

有两种启用的方式

1、 发送一个CSP的http头

1
header("Content-Security-Policy: default-src 'self'; script-src 'self' https://www.kingkk.com;");

2、在meta标签种添加

1
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://www.kingkk.com;">

配置

可以自定义限制不同资源的加载

加载策略(注意一些有引号和一些没引号的区别)

例如之前的那个范例种的CSP策略

1
header("Content-Security-Policy: default-src 'self'; script-src 'self' https://www.kingkk.com;");

就意为script标签只允许加载本站,以及https://www.kingkk.com上的资源,其余的cssimg之类的资源只允许加载本站的

例如服务上的一端代码

1
2
<?php header("Content-Security-Policy: default-src 'self' "); ?>
<img src="https://www.kingkk.com/2018/08/xss%E7%9F%A5%E8%AF%86%E7%82%B9%E5%B0%8F%E8%AE%B0/1.png">

访问时够看到chrome中的报错

需要添加指定CSP源才能添加远程服务器的资源

1
header("Content-Security-Policy: default-src 'self'; script-src 'self'; img-src 'self' https://www.kingkk.com ")

才能正确加载出资源

nonce script

由于默认是会限制内联脚本的执行,然而实际当中内联脚本又是必不可少的,于是推出了一种nonce属性

1
header("Content-Security-Policy: default-src 'self'; script-src 'nonce-{random-str}' ");

只有当script标签中nonce属性值和CSP中的nonce属性值一样才能执行内联脚本

1
<script nonce="{random-str}">alert(1)</script>

这个随机数值需要由后端随机生成。从而减少内联script的xss危害

例如一段这样的代码

1
2
3
4
5
6
<?php 
header("Content-Security-Policy: default-src 'self'; script-src 'self'");
?>
<script>
alert(/xss/);
</script>

可以看到默认是进制这种内联方式的代码的

一种解决方式就是在script-src后面添加'unsafe-inline'

还有就是利用nonce标签,只有值相同的script标签才能执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php 
function random_string( $length = 8 ) {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$password = '';
for($i = 0; $i < $length; $i++)
{
$password .= $chars[ mt_rand(0, strlen($chars) - 1) ];
}
return $password;
}

$random = random_string(12);

header("Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-$random' ");
?>
<script nonce=<?php echo $random; ?>>
alert(/xss/);
</script>

这样就可以成功弹窗

但当这个两个值不匹配时

1
2
3
<script nonce=13456>
alert(/xss/);
</script>

就可以看到控制台的报错信息

由于这个值每次是随机产生的,构造的难度极大提升,从而能较为有效的抵御xss

Reference Link

http://pupiles.com/xss.html

https://lightless.me/archives/review-SOP.html

https://www.hackersb.cn/hacker/85.html

https://paper.seebug.org/423/#0x02-cspcontent-security-policy

http://www.ruanyifeng.com/blog/2016/09/csp.html