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

7.0 KiB
Raw Permalink Blame History

tags, create time
tags create time
计算机网络
SSRF
DNS安全
DNSSEC
DoH
NAT穿透
2026-05-18 05:00

SSRF 与 DNS 安全

概述

SSRF(服务端请求伪造)是近年来增长最快的 Web 漏洞之一,它利用的是"服务器代替用户发起请求"的能力。DNS 层面则有从传统查询到 DoH/DoT 的安全演进。

SSRF(Server-Side Request Forgery)

什么是 SSRF?

当应用程序接受一个 URL 作为输入,并在服务端向该 URL 发起请求时,攻击者可以构造特殊 URL 让服务器访问内部资源(内网 API、元数据服务、数据库端口等)。

典型攻击场景

// ❌ 危险代码:用户控制 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 防御方案

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
}
// 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 ❌
# 检查某域名的 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 最新标准, 低延迟 + 抗干扰
# 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 穿透技术补充

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 的标准框架,同时收集候选地址并按连通性排序。

关联笔记