如何修复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存在的漏洞。

为了修复漏洞,我们需要了解依赖关系。

依赖关系包括,如何知道当前依赖引入了哪些库,以及这些库的版本。如果知道当前的间接依赖对应的直接依赖库,就可以通过升级直接依赖库的版本来修复漏洞。

目前有三种方式查看依赖关系。

通过https://deps.dev/方式在线查看。还是以gin v1.8.1为例。

直接通过https://deps.dev/go/github.com%2Fgin-gonic%2Fgin/v1.8.1/dependencies 就可以查看到gin v1.8.1引入的所有依赖库。

通过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.1testify@v1.8.0

虽然通过这种方式可以看到依赖关系,但是不够直观。通过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工具会直接划线提示当前组件不是最新版本,然后将鼠标移至到划线地方,就会显示最新版本。

Go组件一般都是通过Github进行版本管理的,可以直接在组件对应的Github仓库中找到最新版本。

github.com/gin-gonic/gin为例:

直接查看https://github.com/gin-gonic/gin/releases 就可以知道当前最新版本,直接升级就好。

在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版本

如果对应需要升级的组件已经发布了新版本,那么就直接修改go.mod组件中的版本完成升级就可以。

修改完版本之后,在go.mod的同级目录直接执行go mod tidy就可以更新至最新依赖

直接通过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.modgo 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.modgithub.com/gin-gonic/gin指向本地路径,然后执行go mod tidy更新依赖。

replace github.com/gin-gonic/gin v1.8.1 => /path/to/gin-master

对应的情况是:没有release版本,且直接引用库有没有最新的代码可升级

假设存在一样这样的场景:

  1. 当前cors v1.4.0中的引入的github.com/gin-gonic/gin的版本是v1.8.1,但是当前cors v1.4.0还没有发布新版本
  2. 对应的github.com/gin-gonic/gin已经发布了新版本v1.9.1

那么,我们对应的做法是:

  1. 需要手动下载最新版本的github.com/gin-gonic/gin代码,在本例中是v1.9.1
  2. 修改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项目存在如下的依赖关系,其中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

步步为营,如何将GOlang引用库的安全漏洞修干净