如何修复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