← 返回因果链

🔗 安全攻防因果链

因为…所以… — 从明文传输到风控系统的攻防因果推导

HTTPS密码存储认证 XSSCSRFSQL注入限流

📊 因果链总览图

❓ 明文传输被窃听 💡 HTTPS
❓ 密码明文存储被泄露 💡 哈希→加盐→bcrypt
❓ Session集群共享难 💡 Token→JWT
❓ XSS注入脚本 💡 转义→CSP
❓ CSRF伪造请求 💡 CSRF Token→SameSite
❓ SQL注入 💡 预编译

① 因为:明文传输被窃听 → 所以HTTPS

❓ 问题:HTTP明文传输,中间人可以看一切

你在咖啡店WiFi下登录银行,黑客用Wireshark抓包 → 账号密码一览无余

💡 所以:HTTPS = HTTP + TLS加密

因果链:因为明文可窃听 → 所以需要加密 → 因为对称加密快但密钥怎么传? → 所以用非对称加密传密钥 → 这就是TLS握手

客户端 1.索要公钥 服务器
客户端 2.返回公钥+证书 服务器
客户端 3.用公钥加密对称密钥 服务器
4.之后用对称密钥加密通信(快)

② 因为:密码明文存储被泄露 → 所以加盐哈希

❓ 问题:DB被拖库,所有用户密码暴露
💡 所以:密码不能明文存 → 哈希 → 加盐 → bcrypt
// 因为:明文存储→DB泄露=所有密码暴露 // 所以:哈希→不可逆 password → SHA256 → "5e884898da..." // 但是:彩虹表攻击→预先算好常见密码的哈希→反查 // 所以:加盐(salt)→每个用户不同的随机盐 password + salt → SHA256 → "a7f3c2..." // 但是:SHA256太快→GPU每秒算10亿次→暴力破解 // 所以:bcrypt→故意慢(可调成本因子)→暴力破解不现实 // Spring Security 默认用bcrypt @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); // 默认cost=10 } // 注册时 String encoded = passwordEncoder.encode(rawPassword); // 自动加盐+哈希 // 存encoded到DB // 登录时 boolean match = passwordEncoder.matches(rawPassword, encoded); // 自动验证

③ 因为:Session集群共享难 → 所以Token/JWT

❓ 问题:多台服务器,Session在A服务器,请求到B服务器就丢失
💡 所以:Token——无状态认证

因为Session需要服务端存储 → 集群要共享 → 复杂 → 所以Token让客户端自己携带认证信息

// JWT结构:Header.Payload.Signature // Header: {"alg":"HS256","typ":"JWT"} // Payload: {"userId":123,"exp":1718294400} // Signature: HMACSHA256(base64(header)+"."+base64(payload), secret) // Spring Boot JWT 实现 @Service public class JwtService { @Value("${jwt.secret}") private String secret; public String generateToken(UserDetails user) { return Jwts.builder() .setSubject(user.getUsername()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + 86400000)) .signWith(Keys.hmacShaKeyFor(secret.getBytes()), SignatureAlgorithm.HS256) .compact(); } } // 因为JWT一旦签发无法撤销 → 所以需要黑名单机制 // 因为JWT可以携带信息 → 所以不用查DB就能认证
类比:Cookie = 身份证 / Token = 通行证 / JWT = 加密的通行证

④ 因为:XSS注入脚本 → 所以转义+CSP

❓ 问题:用户输入<script>alert('xss')</script>被浏览器执行
💡 所以:输入校验/转义 + CSP + HttpOnly
// 1. 输出转义(因为:用户输入被当HTML执行 → 所以:转义为文本) // Vue默认转义:{{ }} 自动转义 ✅ // React默认转义:{} 自动转义 ✅ // 危险:v-html / dangerouslySetInnerHTML ❌ // 2. CSP(因为:脚本来源不可控 → 所以:白名单限制脚本来源) Content-Security-Policy: script-src 'self' https://cdn.example.com // 3. HttpOnly(因为:JS能读cookie → 所以:设置HttpOnly禁止JS读取) Set-Cookie: token=xxx; HttpOnly; Secure; SameSite=Strict

⑤ 因为:CSRF伪造请求 → 所以CSRF Token + SameSite

❓ 问题:黑客网站自动发请求到银行转账,浏览器自动带Cookie
💡 所以:CSRF Token + SameSite Cookie

因为Cookie会自动携带 → 所以CSRF Token在表单中,黑客网站拿不到
因为Cookie可以跨站发送 → 所以SameSite=Strict禁止跨站Cookie

⑥ 因为:SQL注入 → 所以预编译

❓ 问题:SELECT * FROM user WHERE name='张三' OR '1'='1'
💡 所以:预编译——参数和SQL结构分离
// ❌ 字符串拼接SQL String sql = "SELECT * FROM user WHERE name='" + name + "'"; // name = "张三' OR '1'='1" → 注入! // ✅ 预编译:参数永远不会被当SQL执行 String sql = "SELECT * FROM user WHERE name = ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, name); // 参数化,不会被解析为SQL // MyBatis: #{}预编译 vs ${}拼接 // #{name} → 预编译 ✅ // ${name} → 拼接 ❌ 只在order by等动态列名时用

⑦ 因为:接口被恶意刷 → 所以限流+验证码

💡 限流因果链:因为接口被刷 → 所以限流 → 因为限流影响正常用户 → 所以验证码区分人机 → 因为验证码体验差 → 所以风控(行为分析)
// Redis 限流:滑动窗口 public boolean isAllowed(String key, int maxCount, int windowSec) { long now = System.currentTimeMillis(); String redisKey = "rate:" + key; redisTemplate.opsForZSet().add(redisKey, "" + now, now); redisTemplate.opsForZSet().removeRangeByScore(redisKey, 0, now - windowSec * 1000); long count = redisTemplate.opsForZSet().zCard(redisKey); return count <= maxCount; }

🎯 面试点映射

面试题因为…所以…
HTTPS原理因为明文可窃听 → 所以TLS加密:非对称传密钥+对称加密通信
密码存储方案因为明文危险 → 哈希 → 因为彩虹表 → 加盐 → 因为GPU快 → bcrypt
Cookie vs Token vs JWT因为Session集群难共享 → Token无状态 → JWT自包含
XSS防御因为用户输入被当HTML执行 → 转义+CSP+HttpOnly
CSRF防御因为Cookie自动携带 → CSRF Token + SameSite
SQL注入防御因为字符串拼接 → 所以预编译:参数和SQL结构分离
限流算法因为接口被刷 → 计数器→滑动窗口→令牌桶

📌 因果链总结

明文
窃听
HTTPS 密码
泄露
bcrypt
加盐哈希
Session
共享难
JWT
无状态
XSS
注入
转义
+CSP
CSRF Token
+SameSite
SQL
注入
预编译
全栈资料库 · 因果链板块 · 安全攻防因果链