ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

gosec 实战指南:Go 代码安全扫描的规则体系、配置详解与 CI 集成

gosec 实战指南:Go 代码安全扫描的规则体系、配置详解与 CI 集成
  • 测试
  • 操作系统
  • 网络安全

【免费下载链接】syzkaller

syzkaller is an unsupervised coverage-guided kernel fuzzer

项目地址:https://gitcode.com/gh_mirrors/syz/syzkaller
点击查看免费下载

本文以 syzkaller 仓库 vendor 目录下随附的 gosec(Go Security Checker) 官方文档为主体,结合仓库内 gosec v2.19.0 的完整源码,系统讲解 gosec 的工作原理、规则体系、配置方式与 CI 落地实践。读完本文,你将掌握 gosec 的安装接入、规则筛选、#nosec抑制注解、SARIF 报告集成等全套实操技能,并能从源码层面理解其 AST + SSA 双通道扫描机制。

gosec 是什么:基于 AST 与 SSA 的 Go 安全扫描器

gosec 通过扫描 Go 源码的AST(抽象语法树)与SSA(静态单赋值)代码表示来检查安全缺陷,属于静态分析(SAST)工具,无需运行被测代码即可发现硬编码凭据、弱加密算法、SQL 注入、不安全的 TLS 配置等典型问题。

从仓库内的源码可以看到其核心机制:

  • 规则接口:vendor/github.com/securego/gosec/v2/rule.go定义了Rule接口(ID()与Match(ast.Node, *Context)),以及RuleBuilder、RuleSet注册表。规则按 AST 节点类型注册,扫描器在遍历 AST 时只对当前节点类型调用已注册的规则。
  • 扫描器主体:vendor/github.com/securego/gosec/v2/analyzer.go中的Analyzer通过ast.Walk遍历语法树驱动规则匹配,同时调用golang.org/x/tools/go/analysis/passes/buildssa构建 SSA 表示,由CheckAnalyzers运行基于数据流的分析器(如切片越界G602即由此实现,见vendor/github.com/securego/gosec/v2/analyzers/slice_bounds.go)。
  • 上下文对象:Context结构体携带文件集、注释映射、类型信息、导入追踪器等数据,在规则匹配时透传给每个规则,使规则不仅能看语法,还能结合类型信息与注释(如#nosec)做判断。

安装 gosec:CI、本地与 Docker 三种方式

CI / 脚本安装

利用 gosec 官方发布脚本可直接将二进制安装到指定目录(脚本会随版本号下载对应平台的预编译产物):

# 二进制将安装到 $(go env GOPATH)/bin/gosec curl -sfL <gosec_install_script_url> | sh -s -- -b $(go env GOPATH)/bin vX.Y.Z # 或安装到当前目录的 ./bin/ curl -sfL <gosec_install_script_url> | sh -s vX.Y.Z # Alpine Linux 默认不带 curl,可用 wget wget -O - -q <gosec_install_script_url> | sh -s vX.Y.Z

如需校验发布包完整性,可下载对应操作系统的gosec_vX.Y.Z_OS.tar.gz压缩包,用 Release 页面提供的校验和文件核对:

echo "<check sum from the check sum file> gosec_vX.Y.Z_OS.tar.gz" | sha256sum -c - gosec --help

本地安装(go install)

在已配置 Go 工具链的环境下,最简单的方式是直接安装:

go install github.com/securego/gosec/v2/cmd/gosec@latest

作为 GitHub Action 运行

securego/gosec@master提供了现成的 Action,最小示例:

name: Run Gosec on: push: branches: - master pull_request: branches: - master jobs: tests: runs-on: ubuntu-latest env: GO111MODULE: on steps: - name: Checkout Source uses: actions/checkout@v3 - name: Run Gosec Security Scanner uses: securego/gosec@master with: args: ./...

与 GitHub code scanning 集成(SARIF 上传)

gosec 支持输出 SARIF 格式报告,可借助 GitHub code scanning 展示扫描结果。将 gosec 作为 workflow 中的一步,输出results.sarif后由upload-sarifAction 上传:

name: "Security Scan" # 每次代码推送时运行,并额外在每周日 00:00 UTC 定时运行 on: push: schedule: - cron: '0 0 * * 0' jobs: tests: runs-on: ubuntu-latest env: GO111MODULE: on steps: - name: Checkout Source uses: actions/checkout@v3 - name: Run Gosec Security Scanner uses: securego/gosec@master with: # 不因扫描结果直接导致任务失败,交由 GitHub Security 特性触发告警 args: '-no-fail -fmt sarif -out results.sarif ./...' - name: Upload SARIF file uses: github/codeql-action/upload-sarif@v2 with: # SARIF 文件路径相对于仓库根目录 sarif_file: results.sarif

Docker 方式

也可构建本地镜像后以容器方式运行(make image构建镜像,细节见 vendor 内 Makefile):

docker run --rm -it -w /<PROJECT>/ -v <YOUR PROJECT PATH>/<PROJECT>:/<PROJECT> securego/gosec /<PROJECT>/...

注意:必须用-w指定容器内工作目录,否则无法正确解析 go module 依赖。

规则体系全景:从 G101 到 G602

gosec 默认会对输入文件运行全部规则。要递归扫描当前目录,以./...作为输入参数即可。完整规则清单如下:

规则 ID检查内容
G101查找硬编码凭据
G102绑定到所有网络接口
G103审计 unsafe 代码块的使用
G104审计未检查的错误
G106审计 ssh.InsecureIgnoreHostKey 的使用
G107作为 taint 输入提供给 HTTP 请求的 URL
G108自动暴露在 /debug/pprof 的性能分析端点
G109strconv.Atoi 结果转换为 int16/32 时潜在的整数溢出
G110解压炸弹导致的潜在 DoS 漏洞
G111潜在的目录穿越
G112潜在的 slowloris 攻击
G113math/big 中 Rat.SetString 的溢出使用(CVE-2022-23772)
G114使用不支持设置超时时间的 net/http serve 函数
G201使用格式化字符串构造 SQL 查询
G202使用字符串拼接构造 SQL 查询
G203在 HTML 模板中使用未转义数据
G204审计命令执行的使用
G301创建目录时使用不当的文件权限
G302使用 chmod 时设置不当的文件权限
G303使用可预测路径创建临时文件
G304作为 taint 输入提供的文件路径
G305解压 zip/tar 归档时的文件遍历
G306写入新文件时使用不当的文件权限
G307使用 os.Create 创建文件时使用不当的文件权限
G401检测 DES、RC4、MD5 或 SHA1 的使用
G402查找糟糕的 TLS 连接设置
G403确保 RSA 密钥长度至少为 2048 位
G404不安全的随机数来源(rand)
G501导入黑名单:crypto/md5
G502导入黑名单:crypto/des
G503导入黑名单:crypto/rc4
G504导入黑名单:net/http/cgi
G505导入黑名单:crypto/sha1
G601range 语句中元素的隐式内存别名
G602切片访问越界

已退休规则(Retired rules)

以下规则已从默认检查中移除:

  • G105:审计 math/big.Int.Exp 的使用——对应 CVE 已修复;
  • G307:defer 调用返回错误的方法——该检查带来的困扰大于其发现的安全价值,故被移除(历史上该编号曾被重新分配用于文件权限检查,注意不同版本语义差异)。

CWE 映射

gosec 发现的每个问题都会映射到对应的CWE(Common Weakness Enumeration)编号,便于与通用漏洞标准对齐。完整映射表定义在仓库源码 vendor/github.com/securego/gosec/v2/issue/issue.go#L55-L91 的ruleToCWE变量中,例如G101 → CWE-798、G201/G202 → CWE-89、G402 → CWE-295、G404 → CWE-338、G601/G602 → CWE-118。扫描结果中的Cwe字段即由此映射生成。

按需选择规则:-include 与 -exclude

默认情况下 gosec 对输入路径运行全部规则,但可通过两个标志精确控制规则集合:

# 只运行指定的规则子集 $ gosec -include=G101,G203,G401 ./... # 运行除 G303 以外的所有规则 $ gosec -exclude=G303 ./...

-include用于从全量规则中挑出关心的子集;-exclude用于剔除误报率较高或与团队规范冲突的规则。从源码看,这一机制对应 analyzer.go 中的RuleSet注册与IsRuleSuppressed查询,被排除的规则在RuleSuppressedMap中标记为 suppressed,命中时不再计入有效 issue。

配置:全局设置与规则级参数

全局配置

通过-conf指定 JSON 配置文件,可设置影响所有规则的全局开关:

{ "global": { "nosec": "enabled", "audit": "enabled" } }
# 使用全局配置文件运行 $ gosec -conf config.json .

两个全局选项的含义:

  • nosec:启用后将覆盖代码库中所有#nosec指令,即不再忽略任何被注解的行;
  • audit:以审计模式运行,启用那些对常规代码分析而言可能过于"多管闲事"的附加检查。

从源码 vendor/github.com/securego/gosec/v2/config.go 可以看到,全局选项还包括show-ignored(被#nosec忽略的 issue 是否仍计入发现)、#nosec(自定义抑制注解替代标签)等,配置值通过IsGlobalEnabled判定("true"或"enabled"视为开启)。

规则级配置:以 G104 为例

某些规则接受专属配置。例如 G104(未检查错误)可指定"包 + 函数"列表,审计时跳过这些函数:

{ "G104": { "ioutil": ["WriteFile"] } }

规则级配置:G101 硬编码凭据

G101 支持自定义匹配模式或调整熵阈值:

{ "G101": { "pattern": "(?i)passwd|pass|password|pwd|secret|private_key|token", "ignore_entropy": false, "entropy_threshold": "80.0", "per_char_threshold": "3.0", "truncate": "32" } }

各参数的作用(结合 rules/hardcoded_credentials.go 源码可确认默认值与判定逻辑):

  • pattern:用于匹配变量名的正则。源码默认值为(?i)passwd|pass|password|pwd|secret|token|pw|apiKey|bearer|cred;
  • ignore_entropy:默认false。为true时跳过熵检测,只要变量名匹配即报警;为false时需同时满足高熵条件;
  • entropy_threshold:默认80.0。字符串整体熵达到该值即判定为高熵(使用zxcvbn口令强度算法计算);
  • per_char_threshold:默认3.0。当总熵超过阈值一半且"每字符熵"达到该值时也判定为高熵;
  • truncate:默认16。参与熵计算的字符串截断长度,避免超长字符串稀释熵值。

除熵检测外,G101 还内置了大量已知凭据格式的正则(secretsPatterns列表),覆盖 RSA/SSH/PGP 私钥块、Slack Token、AWS API Key、GitHub personal access token、Google API Key、Stripe 密钥、Twilio API Key、Telegram Bot Token、URL 内嵌密码等数十种模式。实际命中任一模式即直接报警,无需依赖熵阈值,这也是 G101 误报率相对可控的原因。

扫描实践:依赖、测试文件与生成文件

依赖处理

当启用 go module(如GO111MODULE=on)时,gosec 会自动拉取被分析代码的依赖;否则需要先手动执行go get -d下载依赖后再扫描。

排除测试文件与目录

gosec 默认会忽略所有包中的测试文件以及 vendor 目录中的依赖。若需要扫描测试文件,使用-tests:

gosec -tests ./...

需要排除额外目录时使用-exclude-dir(可重复指定):

gosec -exclude-dir=rules -exclude-dir=cmd ./...

排除生成文件

gosec 可跳过带标准生成注释的 Go 文件。生成注释的标准格式为:

// Code generated by some generator DO NOT EDIT.

配合-exclude-generated标志使用:

gosec -exclude-generated ./...

从源码 analyzer.go 的generatedCodePattern(^// Code generated .* DO NOT EDIT\.$)可以看到其精确匹配规则,CheckRules与CheckAnalyzers两个通道都会跳过此类文件。

#nosec 注解:处理误报的正确姿势

与所有自动化检测工具一样,gosec 也会产生误报。对于已人工确认为安全的告警,可在对应行添加以#nosec开头的注释:

格式为#nosec [RuleList] [-- Justification],注释必须放在告警报告的同一行:

func main() { tr := &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, // #nosec G402 }, } client := &http.Client{Transport: tr} _, err := client.Get("https://golang.org/") if err != nil { fmt.Println(err) } }

进阶用法:

  • 只抑制指定规则:/* #nosec G401 */或//#nosec G201 G202 G203,让该区域只跳过列出的规则,其余规则继续扫描;
  • 附加说明文字:在规则列表后以两个及以上连字符开头书写理由,例如//#nosec G101 G102 -- This is a false positive;
  • 整体忽略所有注解:gosec -nosec=true ./...会忽略所有#nosec,用于复查历史注解是否仍必要。

源码层面,#nosec的解析逻辑在 analyzer.go 的ignore方法中:它按-{2,}切分指令与理由,用G\d{3}正则提取要抑制的规则 ID,若未列出具体规则则抑制该处全部规则(*通配),并记录Kind: "inSource"的抑制信息。

追踪抑制(Track Suppressions)

外部抑制(-include/-exclude)与内联抑制(#nosec)信息可用于审计用途,通过-track-suppressions记录:

gosec -track-suppressions -exclude=G101 -fmt=sarif -out=results.sarif ./...

抑制信息的两种来源:

  • 外部抑制:kind为external,justification固定为 "Globally suppressed."(源码常量externalSuppressionJustification);
  • 内联抑制:kind为inSource,justification为#nosec注释中两个及以上连字符之后的文字。

注意:仅SARIF 和 JSON两种格式支持记录抑制信息。

构建标签与输出格式

构建标签

gosec 可将 Go 构建标签透传给分析器,以逗号分隔列表传入:

gosec -tags debug,ignore ./...

输出格式

gosec 支持text、json、yaml、csv、sonarqube、JUnit XML、html和golint八种输出格式。默认结果输出到 stdout,可用-fmt指定格式、-out指定输出文件:

# 以 json 格式写入 results.json $ gosec -fmt=json -out=results.json *.go

-stdout可在写文件的同时把结果打到标准输出;-verbose则覆盖 stdout 的输出格式:

# 以 json 写入 results.json,同时输出到 stdout $ gosec -fmt=json -out=results.json -stdout *.go # stdout 用 text 格式展示,文件仍写为 json $ gosec -fmt=json -out=results.json -stdout -verbose=text *.go

SonarQube 集成时,gosec 生成通用问题导入格式(generic issue import format),需要将报告通过sonar.externalIssuesReportPaths=path/to/gosec-report.json导入 SonarQube。

从报告结构看(issue/issue.go),每个 Issue 包含severity、confidence、cwe、rule_id、details、file、code、line、column等字段;严重度与置信度均为 LOW/MEDIUM/HIGH 三档(Score枚举),JSON 序列化时输出为对应字符串。

在 syzkaller 仓库中的集成现状

gosec 在本仓库中的角色是静态安全扫描第三方依赖:它以v2.19.0版本作为间接依赖被 vendored(见 go.mod#L187 与 vendor/modules.txt),源码完整保留在 vendor/github.com/securego/gosec/v2 目录下。

在 lint 集成层面,syzkaller 使用 golangci-lint 统一管理静态检查,其配置文件 .golangci.yml#L54 的disable列表中显式禁用了gosec,同时启用了vet、staticcheck、revive、errcheck(注释中说明 errcheck 待修复既有告警后再启用)以及项目自研的syz-linter(自定义插件,路径bin/syz-linter.so)。这表明在大型基础设施类项目(如内核模糊测试框架)中,团队往往会根据自身告警容忍度与既有 lint 体系,选择性地关闭通用安全扫描器,转而依赖更贴合项目语境的规则集——这是 gosec 这类通用工具在实际工程中的常见落地形态。

如果你希望在本仓库启用 gosec 做专项安全审计,可在.golangci.yml的enable列表中加入gosec并按上述-exclude/#nosec机制逐步收敛误报。

开发者视角:构建、测试与发布

构建与测试

# 构建二进制 make # 运行全部单元测试 make test

版本发布与签名校验

发布通过打 tag 触发:

git tag v1.0.0 -m "Release version v1.0.0" git push origin v1.0.0

tag 推送后,发布工作流会调用 goreleaser 产出各平台二进制并发布 Docker 镜像。所有发布产物使用 cosign 签名(公钥见 cosign.pub):

# 校验 Docker 镜像签名 cosign verify --key cosign.pub securego/gosec:<TAG> # 校验二进制包签名 cosign verify-blob --key cosign.pub --signature gosec_<VERSION>_darwin_amd64.tar.gz.sig gosec_<VERSION>_darwin_amd64.tar.gz

TLS 规则自动生成

TLS 规则(G402)的密码套件配置可从 Mozilla 的 TLS 服务端配置推荐自动生成:

# 安装生成工具 go get github.com/securego/gosec/v2/cmd/tlsconfig/... # 在项目根目录执行 go generate ./...

该命令会生成rules/tls_config.go,包含 Mozilla 当前推荐的密码套件清单(仓库中已存在 rules/tls_config.go 生成产物),使 TLS 规则始终与业界推荐基线保持同步。

小结

gosec 通过"AST 规则匹配 + SSA 数据流分析"双通道实现对 Go 代码的安全缺陷检测,提供了从单条命令到 CI/CD 全链路(GitHub Action、SARIF、SonarQube)的集成能力。实践中的关键决策点包括:用-include/-exclude裁剪规则集、用-conf调整全局与规则级参数、用#nosec规范地处理误报、用-track-suppressions沉淀审计证据。无论是为中小型 Go 服务引入第一道安全防线,还是像 syzkaller 这样在既有 lint 体系中权衡取舍,理解 gosec 的规则语义与配置细节都至关重要。

  • 测试
  • 操作系统
  • 网络安全

【免费下载链接】syzkaller

syzkaller is an unsupervised coverage-guided kernel fuzzer

项目地址:https://gitcode.com/gh_mirrors/syz/syzkaller
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进