Files
2026-05-24 11:42:38 +08:00

218 lines
7.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 校验判断