如何编写安全的 Go 代码

在这里插入图片描述

原文:Jakub Jarosz - 2024.11.02

在编写 Go 代码时,如何时刻考虑安全性?要在一篇简短的文章中回答这个问题似乎不太可能。因此,我们将把范围缩小到一些具体做法上。

这些实践如果持续应用,将有助于我们编写健壮、安全且高效的代码。

  • 我们如何获取 Go 安全公告的最新信息?
  • 我们如何保持 Go 代码的补丁和更新?
  • 我们如何针对安全性和稳健性测试 Go 代码?
  • 什么是 CVE,我们在哪里可以了解最常见的软件漏洞?

邮件列表

让我们从最显而易见的地方开始——Go 邮件列表。我们需要订阅它,以便直接从源头获取所有重要的安全信息。包含安全修复的所有发布版本都会通过 golang-announce@googlegroups.com 邮件列表进行公告。一旦我们订阅了该列表,就可以确保不会错过任何重要的公告。

保持 Go 版本的更新

第二步是确保项目中的 Go 版本保持最新。即使我们不使用最新的语言特性,更新 Go 版本也能让我们获得已发现漏洞的所有安全补丁。此外,新的 Go 版本还确保了与新依赖项的兼容性,从而保护我们的应用程序免受潜在的集成问题。

第三步是了解哪些安全问题和 CVE 在哪些 Go 发布版本中得到了修复。我们可以在 Go 发布历史的网站上查看这些信息,然后在项目的 go.mod 文件中更新到最新版本。

在升级到新版本的 Go 之后,我们应确保此操作不会引入兼容性和依赖性问题,特别是在使用第三方包时。这在处理大型项目时可能风险更大,因为这些项目可能有几十甚至上百个直接或间接的包依赖。

关键在于通过消除潜在的依赖性问题来控制风险。这些问题可能包括需要紧急重构现有代码以使其与新依赖项兼容。例如,包、API 或函数签名的变更都可能导致此类问题。

使用 Go 工具

在确认使用没有安全问题的 Go 版本后,我们就可以专注于项目源代码了。我们可以通过使用静态代码分析工具来开始评估代码质量和安全性。

vet

在安装和使用第三方分析工具之前,最好先使用 Go 自带的 go vet 命令。

我们可以使用 go vet 命令来分析 Go 代码。没有参数的 go vet 命令会默认运行所有允许的选项。该工具扫描源代码并报告潜在问题,问题包括代码语法错误和某些可能在程序执行期间引发问题的编程结构。

最常见的问题包括 goroutine 错误、未使用的变量以及代码库中不可达的区域。使用 go vet 命令的主要优势在于它是 Go 工具链的一部分。

在另一篇文章中,我们将深入探讨 vet 的详细信息。更多的文档和示例可以在 go vet 网站 上找到。

staticcheck

Staticcheck 是另一个静态代码分析工具。它是一个第三方 linter(代码检查工具),有助于发现错误并检测可能的性能问题。它还能强制执行 Go 语言的代码格式。Staticcheck 提供代码简化建议,解释发现的问题,并通过示例提出修正建议。

除了将 staticcheck 运行在 CI 流水线中,我们还可以将其作为独立的二进制文件安装在本地电脑上,进行本地代码扫描。让我们安装最新版本:

go install honnef.co/go/tools/cmd/staticcheck@latest

终端没有报错?如果是这样,我们就可以准备运行扫描了。不过首先,需要检查已安装的版本,以确保一切正常。

staticcheck --version
staticcheck 2024.1.1 (0.5.1)

go vet 类似,运行没有参数的 staticcheck 会默认调用所有代码检查器。这种方式与 UNIX 编程哲学的合理默认值相吻合,不强迫用户进行不必要的操作。

让我们看看这个工具能在 NGINX Agent GitHub 仓库中发现什么。首先,我们需要克隆它:

git clone git@github.com:nginx/agent.git

然后,我们可以从项目的根目录运行它:

➜ staticcheck ./...

片刻之后,就可以查看扫描结果了。我们可以将列出的示例分为三类:

  • 已弃用的包、方法或函数,例如:
...
src/core/metrics/sources/cpu.go:111:9: times.Total is deprecated: Total returns the total number of seconds in a CPUTimesStat Please do not use this internal function. (SA1019)
...
test/component/nginx-app-protect/monitoring/monitoring_test.go:15:8: "github.com/golang/protobuf/jsonpb" is deprecated: Use the "google.golang.org/protobuf/encoding/protojson" package instead. (SA1019)
  • 未使用的变量和字段,例如:
src/core/metrics/sources/nginx_plus.go:74:2: field endpoints is unused (U1000)
src/core/metrics/sources/nginx_plus.go:75:2: field streamEndpoints is unused (U1000)
src/core/metrics/sources/nginx_plus_test.go:94:2: var availableZones is unused (U1000)
  • 与代码质量相关的潜在问题,例如:
src/core/nginx.go:791:4: ineffective break statement. Did you mean to break out of the outer loop? (SA4011)

现在,我们可以开始分析这些问题了。由于本篇文章是介绍性文章,深入分析代码库超出了其范围。在后续的文章中,我们将深入分析代码,展示示例,并修复安全性和性能问题。

目前,我们可以先记下 CWE 网站,它包含大量关于这些弱点的信息,供我们以后学习:

  • 未使用的变量 CWE-563
  • 使用过时的函数 CWE-477

golangci-lint

我们将使用的第三个代码分析工具是 golangci-lint。和所有 Go 工具一样,可以通过多种方式安装它,包括使用 go install 命令:

go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest

让我们验证安装是否成功,并检查版本:

golangci-lint --version
golangci-lint has version v1.61.0 built with go1.23.2
...

完美!一切正常。

遵循最小惊讶原则,golangci-lint 在没有参数时会运行所有 linter。

最小惊讶原则:在界面设计中,应始终做出最不令人惊讶的选择。

当我们检查之前克隆的 agent 仓库时会发生什么?golangci-lint 会显示相同的警告和建议吗?让我们找出答案。

和之前一样,从项目根目录开始扫描。

➜ golangci-lint run ./...

几乎立即,我们就注意到了一些改进代码的建议!例如:

src/extensions/nginx-app-protect/monitoring/processor/nap_test.go:60:14: S1025: the argument is already a string, there's no need to use fmt.Sprintf (gosimple)logEntry: fmt.Sprintf(`%s`, func() string {^
src/plugins/common.go:85:5: S1009: should omit nil check; len() for []string is defined as zero (gosimple)if loadedConfig.Extensions != nil && len(loadedConfig.Extensions) > 0 {^

linter 指出了需要注意的确切文件和代码行。我们的任务是评估代码,进行更改,第二次运行 linter 并运行所有单元测试。如果测试通过,我们就可以提交更新后的代码。完成了!当然,我们还需要推送更新到远程仓库。

检测竞态条件

当多个 goroutine 并发访问同一资源时,程序和库中可能出现竞态条件。当至少有一个 goroutine 尝试写入(更改)该资源时,就会检测到这些竞态条件。例如,该资源可能是一个作为计数器的全局或包级变量。这种情况可能会导致程序出现微妙且难以诊断和检测的错误。

Go 原生支持检测这种条件。我们可以使用 Go 的 test 工具并加上 -race 参数来运行测试。此方法将运行竞态检测器并有助于识别并发程序中的问题。

go test -race

需要特别注意的是,检测器只能评估已执行的代码路径,并会忽略未执行的代码路径。因此,首先运行静态代码分析工具并确保项目中没有所谓的 dead code 是至关重要的。

当我们告诉 Go:“嘿,使用 -race 参数运行测试”时,Go 编译器会启用竞态检测器编译代码。然后测试运行,并在运行时检查可能的竞态条件。当检测到竞态时,工具会打印详细报告,显示哪些 goroutine 尝试访问哪些资源。

另一种增加检测并发问题机会的方法是并行运行测试。为此,我们需要在测试中显式添加 t.Parallel()

两个并行执行的测试:

func TestParseDiskSpace(t *testing.T) {t.Parallel()...
func TestParseMemoryUsage(t *testing.T) {t.Parallel()...

检测竞态条件和设计并发代码是一个广泛且令人兴奋的话题,我们将在未来深入讨论。

扫描源代码中的漏洞

govulncheck

有多种工具可以扫描代码库中列于 CVE 数据库中的已知漏洞。

我们默认使用的工具是 govulncheck,它可以确保开发和发布的代码是安全的。可以在开发者的本地机器上安装并在提交代码到远程 Git 仓库之前进行扫描。

此外,还可以将扫描步骤集成到 GitHub 或 GitLab 的 CI 管道中,以便在每次合并请求时自动调用扫描,确保项目中不会引入漏洞。

govulncheck 是由 Go 团队开发的专用扫描器,使用的是一个包含 Go 漏洞的专用数据库。让我们在本地安装 govulncheck 并尝试其基本功能。

要安装最新版本,请运行以下命令:

go install golang.org/x/vuln/cmd/govulncheck@latest

接下来检查安装是否成功:

govulncheck -version
Go: go1.23.2
Scanner: govulncheck@v1.1.3
DB: https://vuln.go.dev
DB updated: 2024-10-17 15:37:30 +0000 UTC
...

现在可以运行首次扫描。让我们克隆 habit Git 仓库,然后进入项目的根目录并运行该工具。

➜ govulncheck
No vulnerabilities found.

看起来不错!我们没有在源代码中发现漏洞。这就结束了吗?还没完全结束!我们最初构建 habit 二进制文件时,go.mod 文件中定义了 Go 1.18 版本,而当前版本是 v1.23.2。

让我们扫描 habit 二进制文件,而不是源代码。

➜ govulncheck -mode binary -show verbose habit

我们在二进制模式下运行 govulncheck,这意味着可以扫描任何我们有权限访问的 Go 二进制文件,而无需源代码!此外,我们使用的是详细模式,它会显示完整的报告。最后的参数是要扫描的二进制文件名称。

嗯!这个报告看起来不太一样!发生了什么?

Scanning your binary for known vulnerabilities...Fetching vulnerabilities from the database...Checking the binary against the vulnerabilities...=== Symbol Results ===No vulnerabilities found.=== Package Results ===Vulnerability #1: GO-2023-2186Incorrect detection of reserved device names on Windows in path/filepathMore info: https://pkg.go.dev/vuln/GO-2023-2186Standard libraryFound in: path/filepath@go1.20.5Fixed in: path/filepath@go1.20.11=== Module Results ===Vulnerability #1: GO-2024-3107Stack exhaustion in Parse in go/build/constraintMore info: https://pkg.go.dev/vuln/GO-2024-3107Standard libraryFound in: stdlib@go1.20.5Fixed in: stdlib@go1.22.7
...Vulnerability #18: GO-2023-1878Insufficient sanitisation of Host header in net/httpMore info: https://pkg.go.dev/vuln/GO-2023-1878Standard libraryFound in: stdlib@go1.20.5Fixed in: stdlib@go1.20.6Your code is affected by 0 vulnerabilities.
This scan also found 1 vulnerability in packages you import and 18
vulnerabilities in modules you require, but your code doesn't appear to call
these vulnerabilities.

报告的第一部分包含最重要的信息:未发现漏洞

其余部分包含在 Go 标准库中发现的其他漏洞的信息。那么我们是否受到了影响?我们的程序是否不安全?

最终的扫描报告告诉我们不必担心。程序似乎并未调用这些漏洞!太好了!

Your code is affected by 0 vulnerabilities.
This scan also found 1 vulnerability in packages you import and 18
vulnerabilities in modules you require, but your code doesn't appear to call
these vulnerabilities.

现在,让我们更新 go.mod 文件,将 Go 版本更改为最新的 1.23。接下来,运行 go mod tidy 以更新所有依赖项。此时,我们准备再次构建二进制文件。

➜ go build -o habit cmd/main.go

让我们重新运行扫描。

➜ govulncheck -mode binary -show verbose habit
Scanning your binary for known vulnerabilities...Fetching vulnerabilities from the database...Checking the binary against the vulnerabilities...No vulnerabilities found.

这就是我们想要的结果!升级了 Go 版本,拉取了依赖项,并验证了软件和依赖项中没有 CVE 漏洞。

gosec

gosec 是一个静态代码分析工具,它可以帮助我们发现不安全的代码构造。可以在本地安装它,或者在 CI 管道中将其作为 GitHub Action 运行。正如之前所述,golangci-lint 包含了 gosec 作为插件,并在每次代码扫描时默认运行它。

让我们尝试一下并在本地安装扫描器。

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

如果没有看到错误,gosec 就可以开始工作了。在运行首次扫描之前,我们先看看帮助说明:

gosec -hgosec - Golang security checkergosec analyses Go source code to look for common programming mistakes that
can lead to security problems.
...

我们可以使用一长串选项和规则来配置扫描器的行为。具体选项的详细信息超出了本文的范围。关于如何配置、运行和利用此 SAST 工具的详细教程即将推出!敬请期待!

为了尝试 gosec,我们需要克隆一个包含 Go 代码的 GitHub 仓库。

让我们克隆 brutus 仓库,这是一个开源的实验性 OSINT 应用程序,用于测试 Web 服务器配置。

git clone git@github.com:CyberRoute/bruter.git

接下来,将当前目录更改为项目的根目录并开始扫描。

➜ gosec ./...

几秒钟后,gosec 会显示扫描报告。我们可以立即学到什么?显示了一份按严重性和置信度排序的潜在问题列表。我们知道代码的哪些部分需要注意,以及问题对应的弱点分类。完美!接下来做什么?

...[/.../bruter/pkg/fuzzer/randomua.go:69] - G404 (CWE-338): Use of weak random number generator (math/rand or math/rand/v2 instead of crypto/rand) (Confidence: MEDIUM, Severity: HIGH)68:> 69:  randomIndex := rand.Intn(len(userAgents))70:  return userAgents[randomIndex]...[/.../bruter/pkg/server/config.go:40] - G402 (CWE-295): TLS InsecureSkipVerify set true. (Confidence: HIGH, Severity: HIGH)39:  customTransport := &http.Transport{> 40:   TLSClientConfig: &tls.Config{InsecureSkipVerify: true},41:  }...

在这个阶段,我们可以检查报告的 CWE,并了解所列出的漏洞的详细信息。例如,第二个问题将我们引导至 CWE-295 网站,在那里可以了解更多关于该漏洞的信息。

模糊测试

检查代码质量和发现漏洞的最后一种方法是模糊测试。模糊测试是一种特殊的自动化测试方法。它利用代码覆盖率来操纵随机生成的输入数据。

模糊测试对于发现潜在的安全缺陷(如缓冲区溢出、SQL 注入、DoS 攻击 和 XSS 攻击)非常有帮助。模糊测试的最重要特性是,它可以自动生成大量的输入组合!开发人员不需要冥思苦想来弄清楚数百甚至数千种输入数据组合!真是太棒了!

我们将在即将推出的教程中更详细地讨论模糊测试。

今天讨论的大多数方法和测试技术都得到了 OpenSSF 基金会的支持。希望获得最佳实践徽章的开源项目需要在许可、变更控制、漏洞报告、质量、安全性以及静态和动态安全代码分析等方面符合 FLOSS 标准。

保持安全,远离 CVE,并享受编程的乐趣!

正如 John Arundel 所说:

“编程很有趣,你应该享受其中的乐趣!”

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/466647.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Go八股(Ⅳ)***slice,string,defer***

***slice,string,defer*** 1.slice和arry的区别 arry: Go语言中arry即为数据的一种集合,需要在声明时指定容量和初值,且一旦声明就长度固定,访问时按照索引访问。通过内置函数len可以获取数组中的元素个…

使用 Mac 数据恢复从 iPhoto 图库中恢复照片

我们每个人都会遇到这种情况:在意识到我们不想丢失照片之前,我们会永久删除 iPhoto 图库中的一些照片。永久删除这些照片后,是否可以从 iPhoto 图库中恢复照片?本文将指导您使用免费的 Mac 数据恢复软件从 iPhoto 中恢复照片。 i…

Spark 的介绍与搭建:从理论到实践

目录 一、分布式的思想 (一)存储 (二)计算 二、Spark 简介 (一)发展历程 (二)Spark 能做什么? (三)spark 的组成部分 (四&…

Spring Boot2(Spring Boot 的Web开发 springMVC 请求处理 参数绑定 常用注解 数据传递 文件上传)

SpringBoot的web开发 静态资源映射规则 总结:只要静态资源放在类路径下: called /static (or /public or /resources or //METAINF/resources 一启动服务器就能访问到静态资源文件 springboot只需要将图片放在 static 下 就可以被访问到了 总结&…

Vue2中使用firefox的pdfjs进行文件文件流预览

文章目录 1.使用场景2. 使用方式1. npm 包下载,[点击查看](https://www.npmjs.com/package/pdfjs-dist)2. 官网下载1. 放到public文件夹下面2. 官网下载地址[点我,进入官网](https://github.com/mozilla/pdf.js/tags?afterv3.3.122) 3. 代码演示4. 图片预览5. 如果遇到跨域或者…

2024软件测试面试热点问题

🍅 点击文末小卡片 ,免费获取软件测试全套资料,资料在手,涨薪更快 大厂面试热点问题 1、测试人员需要何时参加需求分析? 如果条件循序 原则上来说 是越早介入需求分析越好 因为测试人员对需求理解越深刻 对测试工…

C语言 | Leetcode C语言题解之第542题01矩阵

题目: 题解: /*** Return an array of arrays of size *returnSize.* The sizes of the arrays are returned as *returnColumnSizes array.* Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().*/ type…

C++总结

目录 一、面向对象的三大特性 二、引用 2.1 概念 2.2特性 三、类与对象 3.1概念 3.2 类的内容 3.3对象的创建 四、构造函数与析构函数 五、封装 六、继承 6.1概念与基础使用 6.2 继承权限 6.2.1 权限修饰符 6.2.2 继承权限 6.3构造函数 6.3.1 派生类与基类的构造函数关系 6.3.2…

2024 CSS保姆级教程二 - BFC详解

前言 - CSS中的文档流 在介绍BFC之前,需要先给大家介绍一下文档流。​ 我们常说的文档流其实分为定位流、浮动流、普通流三种。​ ​ 1. 绝对定位(Absolute positioning)​ 如果元素的属性 position 为 absolute 或 fixed,它就是一个绝对定位元素。​ 在…

在PHP8内,用Jenssegers MongoDB扩展来实现Laravel与MongoDB的集成

在现代 web 开发中,MongoDB 作为一种流行的 NoSQL 数据库,因其灵活的文档结构和高性能而受到许多开发者的青睐。Laravel,作为一个优雅的 PHP Web 框架,提供了丰富的功能和优雅的代码风格。本文将指导你如何在 Laravel 项目中集成 …

GPU 环境搭建指南:如何在裸机、Docker、K8s 等环境中使用 GPU

本文主要分享在不同环境,例如裸机、Docker 和 Kubernetes 等环境中如何使用 GPU。 跳转阅读原文:GPU 环境搭建指南:如何在裸机、Docker、K8s 等环境中使用 GPU 1. 概述 仅以比较常见的 NVIDIA GPU 举例,系统为 Linux,…

Axure设计之左右滚动组件教程(动态面板)

很多项目产品设计经常会遇到左右滚动的导航、图片展示、内容区域等,接下来我们用Axure来实现一下左右滚动的菜单导航。通过案例我们可以举一反三进行其他方式的滚动组件设计,如常见的上下滚动、翻页滚动等等。 一、效果展示: 1、点击“向左箭…

每天五分钟深度学习框架pytorch:如何加载手写字体数据集mnist?

本文重点 那个这节课程之后,我们就将通过代码的方式来搭建CNN和RNN模型,然后训练,我们使用的数据集为pytorch中已经封装好的数据集,比如mnist,cafir10,本文我们学习一下如何在pytorch中使用它们,然后为之后的章节做准备,现在我们拿mnist来举例。 mnist和cafir10 MIN…

itextpdf打印A5的问题

使用A5打印的时候,再生成pdf是没有问题的。下面做了一个测试,在打印机中,使用A5的纸张横向放入,因为是家用打印机,A5与A4是同一个口,因此只能这么放。 使用itextpdf生成pdf,在浏览器中预览pdf是…

AJAX 全面教程:从基础到高级

AJAX 全面教程:从基础到高级 目录 什么是 AJAXAJAX 的工作原理AJAX 的主要对象AJAX 的基本用法AJAX 与 JSONAJAX 的高级用法AJAX 的错误处理AJAX 的性能优化AJAX 的安全性AJAX 的应用场景总结与展望 什么是 AJAX AJAX(Asynchronous JavaScript and XML…

CKA认证 | Day1 k8s核心概念与集群搭建

第一章 Kubernetes 核心概念 1、主流的容器集群管理系统 容器编排系统: KubernetesSwarmMesos Marathon 2、Kubernetes介绍 Kubernetes是Google在2014年开源的一个容器集群管理系统,Kubernetes简称K8s。 Kubernetes用于容器化应用程序的部署&#x…

web实操1——只使用tomcat发布网站

安装tomcat 下载 肯定是去官网: http://tomcat.apache.org/ 下载之后,解压: !!解压后: logs日志:就是一些输出,输到文本里。 temp:一些临时文件(不用管) webapps:放网站的 work&…

数据结构:七种排序及总结

文章目录 排序一插入排序1直接插入排序2希尔排序二选择排序3直接选择排序4堆排序三 交换排序5冒泡排序6快速排序四 归并排序7归并排序源码 排序 我们数据结构常见的排序有四大种,四大种又分为七小种,如图所示 排序:所谓排序,就是…

A day a tweet(sixteen)——The better way of search of ChatGPT

Introducing ChatGPT search a/ad.及时的/及时地 ChatGPT can now search the web in a much better way than before so you get fast, timely a.有关的(relative n.亲戚,亲属;同类事物 a.比较的;相对的) answers with link…

HTMLCSS:呈现的3D树之美

效果演示 这段代码通过HTML和CSS创建了一个具有3D效果的树的图形&#xff0c;包括分支、树干和阴影&#xff0c;通过自定义属性和复杂的变换实现了较为逼真的立体效果。 HTML <div class"container"><div class"tree"><div class"…