专栏首页用户8870853的专栏使用 gosec 检查 Go 代码中的安全问题

使用 gosec 检查 Go 代码中的安全问题

Go 语言写的代码越来越常见,尤其是在容器、Kubernetes 或云生态相关的开发中。Docker 是最早采用 Golang 的项目之一,随后是 Kubernetes,之后大量的新项目在众多编程语言中选择了 Go。

像其他语言一样,Go 也有它的长处和短处(如安全缺陷)。这些缺陷可能会因为语言本身的缺陷加上程序员编码不当而产生,例如,C 代码中的内存安全问题。

无论它们出现的原因是什么,安全问题都应该在开发过程的早期修复,以免在封装好的软件中出现。幸运的是,静态分析工具可以帮你以更可重复的方式处理这些问题。静态分析工具通过解析用某种编程语言写的代码来找到问题。

这类工具中很多被称为 linter。传统意义上,linter 更注重的是检查代码中编码问题、bug、代码风格之类的问题,它们可能不会发现代码中的安全问题。例如,Coverity 是一个很流行的工具,它可以帮助寻找 C/C++ 代码中的问题。然而,也有一些工具专门用来检查源码中的安全问题。例如,Bandit 可以检查 Python 代码中的安全缺陷。而 gosec 则用来搜寻 Go 源码中的安全缺陷。gosec 通过扫描 Go 的 AST( 抽象语法树(abstract syntax tree))来检查源码中的安全问题。

开始使用 gosec

在开始学习和使用 gosec 之前,你需要准备一个 Go 语言写的项目。有这么多开源软件,我相信这不是问题。你可以在 GitHub 的 热门 Golang 仓库中找一个。

本文中,我随机选了 Docker CE 项目,但你可以选择任意的 Go 项目。

安装 Go 和 gosec

如果你还没安装 Go,你可以先从仓库中拉取下来。如果你用的是 Fedora 或其他基于 RPM 的 Linux 发行版本:

$ dnf install golang.x86_64

如果你用的是其他操作系统,请参照 Golang 安装页面。

使用 version 参数来验证 Go 是否安装成功:

$ go version
go version go1.14.6 linux/amd64

运行 go get 命令就可以轻松地安装 gosec

$ go get github.com/securego/gosec/cmd/gosec

上面这行命令会从 GitHub 下载 gosec 的源码,编译并安装到指定位置。在仓库的 README 中你还可以看到安装该工具的其他方法

gosec 的源码会被下载到 $GOPATH 的位置,编译出的二进制文件会被安装到你系统上设置的 bin 目录下。你可以运行下面的命令来查看 $GOPATH$GOBIN 目录:

$ go env | grep GOBIN
GOBIN="/root/go/gobin"
$ go env | grep GOPATH
GOPATH="/root/go"

如果 go get 命令执行成功,那么 gosec 二进制应该就可以使用了:

$ ls -l ~/go/bin/
total 9260
-rwxr-xr-x. 1 root root 9482175 Aug 20 04:17 gosec

你可以把 $GOPATH 下的 bin 目录添加到 $PATH 中。这样你就可以像使用系统上的其他命令一样来使用 gosec 命令行工具(CLI)了。

$ which gosec
/root/go/bin/gosec
$

使用 gosec 命令行工具的 -help 选项来看看运行是否符合预期:

$ gosec -help

gosec - Golang security checker

gosec analyzes Go source code to look for common programming mistakes that
can lead to security problems.

VERSION: dev
GIT TAG:
BUILD DATE:

USAGE:

之后,创建一个目录,把源码下载到这个目录作为实例项目(本例中,我用的是 Docker CE):

$ mkdir gosec-demo
$ cd gosec-demo/
$ pwd
/root/gosec-demo
$ git clone https://github.com/docker/docker-ce.git
Cloning into 'docker-ce'...
remote: Enumerating objects: 1271, done.
remote: Counting objects: 100% (1271/1271), done.
remote: Compressing objects: 100% (722/722), done.
remote: Total 431003 (delta 384), reused 981 (delta 318), pack-reused 429732
Receiving objects: 100% (431003/431003), 166.84 MiB | 28.94 MiB/s, done.
Resolving deltas: 100% (221338/221338), done.
Updating files: 100% (10861/10861), done.

代码统计工具(本例中用的是 cloc)显示这个项目大部分是用 Go 写的,恰好迎合了 gosec 的功能。

$ ./cloc /root/gosec-demo/docker-ce/
   10771 text files.
    8724 unique files.                                          
    2560 files ignored.


-----------------------------------------------------------------------------------
Language                         files          blank        comment           code
-----------------------------------------------------------------------------------
Go                                7222         190785         230478        1574580
YAML                                37           4831            817         156762
Markdown                           529          21422              0          67893
Protocol Buffers                   149           5014          16562          10071

使用默认选项运行 gosec

在 Docker CE 项目中使用默认选项运行 gosec,执行 gosec ./... 命令。屏幕上会有很多输出内容。在末尾你会看到一个简短的 “Summary”,列出了浏览的文件数、所有文件的总行数,以及源码中发现的问题数。

$ pwd
/root/gosec-demo/docker-ce
$ time gosec ./...
[gosec] 2020/08/20 04:44:15 Including rules: default
[gosec] 2020/08/20 04:44:15 Excluding rules: default
[gosec] 2020/08/20 04:44:15 Import directory: /root/gosec-demo/docker-ce/components/engine/opts
[gosec] 2020/08/20 04:44:17 Checking package: opts
[gosec] 2020/08/20 04:44:17 Checking file: /root/gosec-demo/docker-ce/components/engine/opts/address_pools.go
[gosec] 2020/08/20 04:44:17 Checking file: /root/gosec-demo/docker-ce/components/engine/opts/env.go
[gosec] 2020/08/20 04:44:17 Checking file: /root/gosec-demo/docker-ce/components/engine/opts/hosts.go

# End of gosec run

Summary:
   Files: 1278
   Lines: 173979
   Nosec: 4
  Issues: 644

real    0m52.019s
user    0m37.284s
sys     0m12.734s
$

滚动屏幕你会看到不同颜色高亮的行:红色表示需要尽快查看的高优先级问题,黄色表示中优先级的问题。

关于误判

在开始检查代码之前,我想先分享几条基本原则。默认情况下,静态检查工具会基于一系列的规则对测试代码进行分析,并报告出它们发现的所有问题。这是否意味着工具报出来的每一个问题都需要修复?非也。这个问题最好的解答者是设计和开发这个软件的人。他们最熟悉代码,更重要的是,他们了解软件会在什么环境下部署以及会被怎样使用。

这个知识点对于判定工具标记出来的某段代码到底是不是安全缺陷至关重要。随着工作时间和经验的积累,你会慢慢学会怎样让静态分析工具忽略非安全缺陷,使报告内容的可执行性更高。因此,要判定 gosec 报出来的某个问题是否需要修复,让一名有经验的开发者对源码做人工审计会是比较好的办法。

高优先级问题

从输出内容看,gosec 发现了 Docker CE 的一个高优先级问题,它使用的是低版本的 TLS( 传输层安全(Transport Layer Security)())。无论什么时候,使用软件和库的最新版本都是确保它更新及时、没有安全问题的最好的方法。

[/root/gosec-demo/docker-ce/components/engine/daemon/logger/splunk/splunk.go:173] - G402 (CWE-295): TLS MinVersion too low. (Confidence: HIGH, Severity: HIGH)
    172:
  > 173:        tlsConfig := &tls.Config{}
    174:

它还发现了一个弱随机数生成器。它是不是一个安全缺陷,取决于生成的随机数的使用方式。

[/root/gosec-demo/docker-ce/components/engine/pkg/namesgenerator/names-generator.go:843] - G404 (CWE-338): Use of weak random number generator (math/rand instead of crypto/rand) (Confidence: MEDIUM, Severity: HIGH)
    842: begin:
  > 843:        name := fmt.Sprintf("%s_%s", left[rand.Intn(len(left))], right[rand.Intn(len(right))])
    844:        if name == "boring_wozniak" /* Steve Wozniak is not boring */ {

中优先级问题

这个工具还发现了一些中优先级问题。它标记了一个通过与 tar 相关的解压炸弹这种方式实现的潜在的 DoS 威胁,这种方式可能会被恶意的攻击者利用。

[/root/gosec-demo/docker-ce/components/engine/pkg/archive/copy.go:357] - G110 (CWE-409): Potential DoS vulnerability via decompression bomb (Confidence: MEDIUM, Severity: MEDIUM)
    356:
  > 357:                        if _, err = io.Copy(rebasedTar, srcTar); err != nil {
    358:                                w.CloseWithError(err)

它还发现了一个通过变量访问文件的问题。如果恶意使用者能访问这个变量,那么他们就可以改变变量的值去读其他文件。

[/root/gosec-demo/docker-ce/components/cli/cli/context/tlsdata.go:80] - G304 (CWE-22): Potential file inclusion via variable (Confidence: HIGH, Severity: MEDIUM)
    79:         if caPath != "" {
  > 80:                 if ca, err = ioutil.ReadFile(caPath); err != nil {
    81:                         return nil, err

文件和目录通常是操作系统安全的最基础的元素。这里,gosec 报出了一个可能需要你检查目录的权限是否安全的问题。

[/root/gosec-demo/docker-ce/components/engine/contrib/apparmor/main.go:41] - G301 (CWE-276): Expect directory permissions to be 0750 or less (Confidence: HIGH, Severity: MEDIUM)
    40:         // make sure /etc/apparmor.d exists
  > 41:         if err := os.MkdirAll(path.Dir(apparmorProfilePath), 0755); err != nil {
    42:                 log.Fatal(err)

你经常需要在源码中启动命令行工具。Go 使用内建的 exec 库来实现。仔细地分析用来调用这些工具的变量,就能发现安全缺陷。

[/root/gosec-demo/docker-ce/components/engine/testutil/fakestorage/fixtures.go:59] - G204 (CWE-78): Subprocess launched with variable (Confidence: HIGH, Severity: MEDIUM)
    58:
  > 59:              cmd := exec.Command(goCmd, "build", "-o", filepath.Join(tmp, "httpserver"), "github.com/docker/docker/contrib/httpserver")
    60:                 cmd.Env = append(os.Environ(), []string{

低优先级问题

在这个输出中,gosec 报出了一个 unsafe 调用相关的低优先级问题,这个调用会绕开 Go 提供的内存保护。再仔细分析下你调用 unsafe 的方式,看看是否有被别人利用的可能性。

[/root/gosec-demo/docker-ce/components/engine/pkg/archive/changes_linux.go:264] - G103 (CWE-242): Use of unsafe calls should be audited (Confidence: HIGH, Severity: LOW)
    263:        for len(buf) > 0 {
  > 264:                dirent := (*unix.Dirent)(unsafe.Pointer(&buf[0]))
    265:                buf = buf[dirent.Reclen:]



[/root/gosec-demo/docker-ce/components/engine/pkg/devicemapper/devmapper_wrapper.go:88] - G103 (CWE-242): Use of unsafe calls should be audited (Confidence: HIGH, Severity: LOW)
    87: func free(p *C.char) {
  > 88:         C.free(unsafe.Pointer(p))
    89: }

它还标记了源码中未处理的错误。源码中出现的错误你都应该处理。

[/root/gosec-demo/docker-ce/components/cli/cli/command/image/build/context.go:172] - G104 (CWE-703): Errors unhandled. (Confidence: HIGH, Severity: LOW)
    171:                err := tar.Close()
  > 172:                os.RemoveAll(dockerfileDir)
    173:                return err

自定义 gosec 扫描

使用 gosec 的默认选项会带来很多的问题。然而,经过人工审计,随着时间推移你会掌握哪些问题是不需要标记的。你可以自己指定排除和包含哪些测试。

原文链接:https://zhuanlan.zhihu.com/p/261663439

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 免费开源的代码审计工具 Gosec 入门使用

    本教程是在自己的电脑上本地测试Gosec的效果,所以不涉及其他运行模式,如果想要了解其他模式可以关注后期文档,如果想要自定义交流自定义代码扫描规则,可以跟我交流...

    BigYoung小站
  • 一些比较好的golang安全项目

    今天主要是推荐一些比较好的golang安全项目,帮助大家取好好学习怎么自己写一个NB的安全工具。

    七夜安全博客
  • Xcheck之Golang安全检查引擎

    Go语言近几年开始越来越流行,凭借其强大的性能和跨平台的优势,对web和后台开发都是一个不错的选择。Xcheck支持Golang的代码安全检查,覆盖常用web框...

    腾讯代码安全检查Xcheck
  • Gitlab的“DevSecOps发展蓝图”概览

    印象中,Gitlab作为Github的竞品,是一款“仓库管理系统”。但,士别三日,当刮目相看。

    用户1713112
  • AngularJS 使用$sce控制代码安全检查

    由于浏览器都有同源加载策略,不能加载不同域下的文件、也不能使用不合要求的协议比如file进行访问。 在angularJs中为了避免安全漏洞,一些ng-src...

    用户1154259
  • 有赞 GO 项目单测、集成、增量覆盖率统计与分析

    我是一名中间件 QA,我对应的研发团队是有赞 PaaS,目前我们团队有很多产品是使用 go 语言开发,因此我对 go 语言项目的单测覆盖率、集成以及增量测试覆盖...

    有赞coder
  • 使用Java8中的Optional类来消除代码中的null检查

    lw900925.github.io/java/java8-optional.html

    JAVA葵花宝典
  • EasyNVR使用过程中问题的自我排查-----设备不在线问题自我排查检测

    由于EasyNVR的受众越来越多,时长会遇到很对类似的问题咨询,之前虽然有写过很多的博文进行技术的或者使用问题的解答,随着客户询问的增多,我发现,要想然客户了解...

    EasyNVR
  • 梳理前端开发使用 eslint 和 prettier 来检查和格式化代码问题

    前端正义联盟
  • 【作者投稿】PHP代码审计-sprintf函数中的安全问题

    看到一篇WorldPress注入漏洞分析,其中sprintf单引号逃逸的思路很巧妙,在此对这类函数做一些简单的测试和总结。

    信安之路
  • 7个顶级静态代码分析工具

    静态代码分析或源代码分析是指使用静态代码分析工具对软件的“静态”(不运行的) 代码进行分析的一种方法,找出代码中潜在的漏洞。静态代码分析器检查源代码,找出特定的...

    深度学习与Python
  • 云计算中使用虚拟化面临的安全问题

    在云计算中,有三种基本服务模式:软件即服务(SaaS)、平台即服务(PaaS)和基础架构即服务(IaaS)。此外,还有三种基本部署模式:公共、混合和私有云计算。...

    静一
  • Go通关20:代码检查与优化!

    代码规范检查,是根据 Go 语言的规范,对代码进行 「静态扫描检查」,这种检查和业务没有关系。 比如程序中定义了个常量,从未使用过,虽然代码运行没有什么影响,但...

    微客鸟窝
  • EasyNVR运行日志报错fatal error: concurrent map read and map write排查

    大家知道我们的产品大多用Go语言编译,Go语言中的 map 在并发情况下,只读是线程安全的,同时读写是线程不安全的。

    EasyNVR
  • EasyNVR运行日志报错fatal error,该如何处理?

    大家知道我们的EasyNVR用Go语言编译,Go语言中的 map 在并发情况下,只读是线程安全的,同时读写是线程不安全的。

    TSINGSEE青犀视频
  • 使用Jenkins + git submodule 实现自动化编译,解决代码安全性问题

    事情发生在功能机的时代,我们项目组开发一款手机,软件开发成员大概有 20 人左右吧。结果在手机发布的一周后,另一家小厂就推出了软件界面、功能几乎完全一样的手机,...

    IOT物联网小镇
  • Goland 中配置 go-lint 代码检查

    go-lint 是由go官方提供的一个代码审查及问题提示的工具.在vscode 中,如果安装了go-lint 则会在终端的问题中显示所有的代码不规范的地方及优化...

    caoayu
  • 如何使用TFsec来对你的Terraform代码进行安全扫描

    TFsec是一个专门针对Terraform代码的安全扫描工具,该工具能够对Terraform模板执行静态扫描分析,并检查出潜在的安全问题,当前版本的TFsec支...

    FB客服
  • 【Rust日报】 2019-08-07:「讨论」Rust的安全性

    今天的讨论有两个主题,第一个是关于在Safe Rust中绕过借用检查的一个问题,第二个是关于Libra区块链依赖Rust单一语言而引起的安全性讨论。

    MikeLoveRust

扫码关注云+社区

领取腾讯云代金券