阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > CSRF 攻击的基本原理

CSRF 攻击的基本原理

作者:陈川 阅读数:36137人阅读 分类: 前端安全

CSRF(Cross-Site Request Forgery)攻击是一种利用用户已登录的身份,在用户不知情的情况下执行非预期操作的攻击方式。攻击者通过诱导用户访问恶意页面或点击链接,触发对目标站点的请求,从而以用户权限完成非法操作。

CSRF 攻击的核心机制

CSRF 攻击依赖于浏览器自动携带用户凭证(如 Cookie)的机制。当用户登录某个网站后,浏览器会存储会话 Cookie,并在后续请求中自动附加这些 Cookie。攻击者利用这一特性,构造一个恶意请求,诱使用户在已登录状态下触发该请求。

关键条件包括:

  1. 用户已登录目标站点并保持会话
  2. 目标站点未采用有效的 CSRF 防护措施
  3. 用户被诱导执行了攻击者预设的操作

典型攻击场景分析

银行转账案例

假设某银行网站的转账接口如下:

POST /transfer HTTP/1.1
Host: bank.example.com
Cookie: sessionid=user123
Content-Type: application/x-www-form-urlencoded

toAccount=attacker&amount=1000

攻击者可以在自己的恶意网站中嵌入:

<form action="https://bank.example.com/transfer" method="POST">
  <input type="hidden" name="toAccount" value="attacker">
  <input type="hidden" name="amount" value="1000">
</form>
<script>
  document.forms[0].submit();
</script>

当已登录银行网站的用户访问该恶意页面时,转账请求会自动发送并携带用户的会话 Cookie。

社交网站修改设置

某社交网站的邮箱修改接口:

// 前端代码
fetch('/api/update-email', {
  method: 'POST',
  body: JSON.stringify({ newEmail: 'attacker@example.com' }),
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${localStorage.token}`
  }
})

攻击者可以构造钓鱼页面:

<a href="https://social.com/api/update-email?newEmail=attacker@example.com">
  点击领取优惠券
</a>

CSRF 与 XSS 的区别

虽然都涉及跨站脚本,但两者有本质区别:

特性 CSRF XSS
攻击目标 利用用户身份执行操作 窃取用户数据或会话
依赖条件 需要用户已登录 需要存在注入漏洞
请求发起方 用户浏览器自动发起 恶意脚本主动执行
数据流向 从目标站点向外发出请求 外部脚本获取站点数据

防御措施的实现方案

同源检测

// 服务端中间件示例(Node.js)
app.use((req, res, next) => {
  const origin = req.headers.origin || req.headers.referer;
  if (!origin || !origin.match(/^https?:\/\/trusted\.com$/)) {
    return res.status(403).send('Forbidden');
  }
  next();
});

CSRF Token 方案

<!-- 服务端渲染时注入Token -->
<form action="/transfer" method="POST">
  <input type="hidden" name="_csrf" value="<%= csrfToken %>">
  <!-- 其他表单字段 -->
</form>

服务端验证:

// Express.js 使用 csrf 中间件
const csrf = require('csurf');
app.use(csrf({ cookie: true }));

app.post('/transfer', (req, res) => {
  // 中间件会自动验证_csrf字段
});

SameSite Cookie 属性

// 设置Cookie时添加SameSite属性
res.cookie('sessionid', 'abc123', {
  httpOnly: true,
  secure: true,
  sameSite: 'Strict'
});

双重提交Cookie

前端代码:

// 从Cookie中读取CSRF Token
function getCookie(name) {
  const value = `; ${document.cookie}`;
  const parts = value.split(`; ${name}=`);
  if (parts.length === 2) return parts.pop().split(';').shift();
}

// 发送请求时附加Header
fetch('/api/transfer', {
  method: 'POST',
  headers: {
    'X-CSRF-TOKEN': getCookie('csrf_token')
  }
});

现代前端框架的防护机制

React 的 CSRF 防护

// 在全局请求拦截器中添加Token
axios.interceptors.request.use(config => {
  config.headers['X-XSRF-TOKEN'] = Cookies.get('XSRF-TOKEN');
  return config;
});

Angular 的HttpClient

// 启用CSRF防护
import { HttpClientXsrfModule } from '@angular/common/http';

@NgModule({
  imports: [
    HttpClientXsrfModule.withOptions({
      cookieName: 'XSRF-TOKEN',
      headerName: 'X-XSRF-TOKEN'
    })
  ]
})
export class AppModule {}

实际开发中的注意事项

  1. 敏感操作二次验证
// 关键操作要求重新输入密码
app.post('/delete-account', (req, res) => {
  if (!verifyPassword(req.body.password)) {
    return res.status(401).send('Invalid password');
  }
  // 执行删除操作
});
  1. API设计原则
  • 对状态修改操作强制使用POST/PUT/DELETE方法
  • 避免使用GET请求执行写操作
  • 为RESTful API添加版本控制前缀
  1. 日志监控
// 记录敏感操作日志
app.post('/transfer', (req, res) => {
  logAction({
    userId: req.user.id,
    action: 'TRANSFER',
    metadata: {
      toAccount: req.body.toAccount,
      amount: req.body.amount,
      ip: req.ip
    }
  });
});

高级攻击场景应对

针对AJAX请求的防护

// 自定义Header防护
const csrfToken = generateToken();
localStorage.setItem('csrf-token', csrfToken);

fetch('/api/data', {
  headers: {
    'X-Custom-CSRF': csrfToken
  }
});

文件上传的CSRF防护

<!-- 文件上传表单需要特殊处理 -->
<form id="upload-form">
  <input type="file" name="file">
  <input type="hidden" name="_csrf" value="token123">
  <button onclick="uploadWithProgress()">上传</button>
</form>

<script>
function uploadWithProgress() {
  const formData = new FormData(document.getElementById('upload-form'));
  // 确保Content-Type不被覆盖
  fetch('/upload', {
    method: 'POST',
    body: formData,
    headers: {
      'X-CSRF-Verify': getCSRFToken()
    }
  });
}
</script>

移动端特有的CSRF问题

深度链接攻击

攻击者可以构造如下URL:

myapp://transfer?to=attacker&amount=1000

防御方案:

// Android Intent Filter验证
<intent-filter>
  <data android:scheme="https" android:host="verified.domain"/>
  <action android:name="android.intent.action.VIEW"/>
  <category android:name="android.intent.category.DEFAULT"/>
  <category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>

WebView中的防护

// 配置WebView的安全策略
webView.settings.javaScriptEnabled = true
webView.webViewClient = object : WebViewClient() {
    override fun shouldOverrideUrlLoading(
        view: WebView?,
        request: WebResourceRequest?
    ): Boolean {
        // 验证URL域名白名单
        if (!isTrustedDomain(request?.url?.host)) {
            return true
        }
        return false
    }
}

浏览器安全策略演进

Chrome 的SameSite默认变更

自Chrome 80起,Cookie的SameSite属性默认为Lax

  • Strict: 完全禁止跨站发送
  • Lax: 允许顶级导航的GET请求
  • None: 允许跨站发送(需配合Secure)

Fetch Metadata规范

利用Sec-Fetch-*请求头进行防护:

# Nginx配置示例
location /api/ {
    if ($http_sec_fetch_site != "same-origin") {
        return 403;
    }
    # 其他处理逻辑
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌