如何修复Golang引用库的安全漏洞
前言
Golang中最常用的依赖管理工具是go mod,但是在使用go mod的过程中,我们可能会遇到一些问题,比如依赖库的安全漏洞。本文将介绍如何修复Golang引用库的安全漏洞,包括直接依赖和间接依赖导致的漏洞。
漏洞查看
如何知道一个Golang
仓库有是否存在漏洞,可以借助https://deps.dev/
查看。
例如,可以利用deps
查看github.com/gin-gonic/gin
各个版本漏洞情况。
访问https://deps.dev/go/github.com%2Fgin-gonic%2Fgin/v1.8.1
,就会显示当前gin v1.8.1
存在的漏洞。
查看依赖关系
为了修复漏洞,我们需要了解依赖关系。
依赖关系包括,如何知道当前依赖引入了哪些库,以及这些库的版本。如果知道当前的间接依赖对应的直接依赖库,就可以通过升级直接依赖库的版本来修复漏洞。
目前有三种方式查看依赖关系。
通过deps
查看
通过https://deps.dev/
方式在线查看。还是以gin v1.8.1
为例。
直接通过https://deps.dev/go/github.com%2Fgin-gonic%2Fgin/v1.8.1/dependencies
就可以查看到gin v1.8.1
引入的所有依赖库。
通过graph
查看
通过https://deps.dev/
,在github.com/gin-contrib/cors v1.4.0
中存在一个github.com/gin-gonic/gin v1.8.1
的依赖关系。
通过go mod graph| grep cors
命令就可以看到当前项目的依赖关系。
$ go mod graph | grep cors
go-sca-test github.com/gin-contrib/cors@v1.4.0
github.com/gin-contrib/cors@v1.4.0 github.com/gin-gonic/gin@v1.8.1
github.com/gin-contrib/cors@v1.4.0 github.com/stretchr/testify@v1.8.0
可以看到在cors@v1.4.0
组件中,依赖了gin@v1.8.1
和testify@v1.8.0
。
通过gmchart
查看
虽然通过这种方式可以看到依赖关系,但是不够直观。通过gmchart
可以直接通过html网页的方式展示当前项目所有的依赖关系。
gmchart官网地址:https://github.com/PaulXu-cn/go-mod-graph-chart
gmchart安装方法:
go install github.com/PaulXu-cn/go-mod-graph-chart/gmchart@latest
安装成功之后,直接通过go mod graph | gmchart
,就可以通过网页以树状图的方式查看当前项目的所有依赖关系。
通过gmchart
的方式就非常的直观。
如何查看新版本
如果是当前的组件存在漏洞,那么在没有兼容性问题下直接升级到最新版本就行。有以下几种方式可以知道当前组件的最新版本。
Goland提示
如果当前组件版本不是最新版本,Goland工具会直接划线提示当前组件不是最新版本,然后将鼠标移至到划线地方,就会显示最新版本。
Github查看
Go组件一般都是通过Github
进行版本管理的,可以直接在组件对应的Github仓库中找到最新版本。
以github.com/gin-gonic/gin
为例:
直接查看https://github.com/gin-gonic/gin/releases
就可以知道当前最新版本,直接升级就好。
go list命令查看
在go.mod同级目录下,执行:
go list -m -mod=mod -u all
这条命令会列出所有直接引用库以及它们的版本,并且会标出哪些包需要升级。
$ go list -m -mod=mod -u all
github.com/gin-gonic/gin v1.8.1 [v1.10.0]
上面就是执行命令之后,一个简单的演示效果。中括号里面显示的就是,可以升级的最新release版本
升级方法
修改mod文件升级
如果对应需要升级的组件已经发布了新版本,那么就直接修改go.mod
组件中的版本完成升级就可以。
修改完版本之后,在go.mod
的同级目录直接执行go mod tidy
就可以更新至最新依赖
通过get方式升级
直接通过go get -u <package-name>
的方式升级到最新版本。
使用@latest
更新至当前最新版本
go get -u github.com/gin-gonic/gin@latest
使用@1.10.0
更新至指定1.10.0
版本
go get -u github.com/gin-gonic/gin@1.10.0
最终执行效果如下所示:
$ go get -u github.com/gin-gonic/gin@latest
go: downloading github.com/ugorji/go v1.2.12
go: downloading github.com/klauspost/cpuid v1.3.1
go: downloading github.com/go-playground/validator v9.31.0+incompatible
go: downloading golang.org/x/term v0.20.0
go: added github.com/bytedance/sonic v1.11.6
go: added github.com/bytedance/sonic/loader v0.1.1
go: added github.com/cloudwego/base64x v0.1.4
go: added github.com/cloudwego/iasm v0.2.0
go: upgraded github.com/gabriel-vasile/mimetype v1.4.2 => v1.4.3
go: upgraded github.com/gin-gonic/gin v1.8.1 => v1.10.0
可以看到其中的执行日志,go: upgraded github.com/gin-gonic/gin v1.8.1 => v1.10.0
将当前系统中的gin v1.8.1
升级到了最新版本v1.10.0
手动下载最新代码到本地
有时候会出现组件代码已经更新,但是没有发布release版本,导致无法直接通过修改go.mod
和go get
的方式下载到最新的依赖。
此时我们只能直接下载对应组件的最新代码放在本地,一般情况下golang项目所有的依赖全部是放在$GOPATH/pkg/mod
中。
如果是从github.com
上下载的依赖,那么对应组件的存储位置就是$GOPATH/pkg/mod/github.com
以github.com/gin-gonic/gin v1.8.1
为例,在本地的文件路径是$GOPATH/pkg/mod/github.com/gin-gonic/gin@v1.8.1
可以将最新的github.com/gin-gonic/gin
代码下载到本地,放在某个目录下,比如是/path/to/gin-master
下载完成之后,修改go.mod
将github.com/gin-gonic/gin
指向本地路径,然后执行go mod tidy
更新依赖。
replace github.com/gin-gonic/gin v1.8.1 => /path/to/gin-master
修改间接依赖库的版本
对应的情况是:没有release版本,且直接引用库有没有最新的代码可升级
假设存在一样这样的场景:
- 当前
cors v1.4.0
中的引入的github.com/gin-gonic/gin
的版本是v1.8.1
,但是当前cors v1.4.0
还没有发布新版本 - 对应的
github.com/gin-gonic/gin
已经发布了新版本v1.9.1
那么,我们对应的做法是:
- 需要手动下载最新版本的
github.com/gin-gonic/gin
代码,在本例中是v1.9.1
- 修改
cors v1.4.0
中的go.mod
对应的gin
版本的配置
可以将最新的github.com/gin-gonic/gin
代码下载到本地,放在某个目录下,比如是/path/to/gin-master
。
修改cors v1.4.0
中的go.mod
配置,这个文件一般是在$GOPATH/go/pkg/mod/github.com/gin-contrib/cors@v1.4.0/go.mod
直接修改为最新版本,如下所示
replace github.com/gin-gonic/gin v1.8.1 => /path/to/gin-master
然后执行go.mod
代码,重新更新项目的依赖信息,如果最终可以编译通过,那么就说明修改版本的配置已经生效了。
Golang最小版本选择原则
比如Golang项目存在如下的依赖关系,其中cors v1.4.0
中引用了gin v1.8.1
的版本
require (
github.com/gin-contrib/cors v1.4.0
github.com/gin-gonic/gin v1.10.0
)
按照最小版本选择原则,Go会选择所有指定依赖中的最高版本。因此,尽管cors
库指定了对gin v1.8.1
的依赖,实际上会使用gin v1.10.0
,因为它是被直接指定在你的go.mod
文件中的最高版本。
通过 go list -m all
来列出所有选择的模块及其版本。当前项目使用的gin
版本是gin v1.10.0
。
$ go list -m all | grep gin
github.com/gin-contrib/cors v1.4.0
github.com/gin-contrib/sse v0.1.0
github.com/gin-gonic/gin v1.10.0