Google I/O 2023 : Build more secure apps with Go and Google
Google I/O 2023 中有個議題關於 Go 的安全性. Build more secure apps with Go and Google. 在這邊簡單做了一下筆記.
What is Software Supply Chain Security
軟體供應鏈安全(Software Supply Chain Security)是指保護和確保軟體開發和分發過程的安全性。軟體供應鏈是指從軟體開發的起點到最終部署和使用的整個過程中所涉及的各個環節和相關方。軟體供應鏈安全的目標是防止或減輕軟體供應鏈中的各種風險和威脅,包括但不限於恶意軟體注入、未經授權的更改、依賴風險、開源軟體漏洞等。
軟體供應鏈安全的關鍵是確保整個供應鏈中的每個環節都具備適當的安全措施和防護措施。這包括進行供應鏈可信度驗證、實施適當的身份驗證和存取控制、進行源代碼和二進制代碼的審查和測試、使用加密技術保護敏感數據、確保第三方組件的可靠性和安全性等等。
軟體供應鏈安全是一個綜合性的問題,需要從整個開發和部署過程的角度進行考慮和管理。這包括軟體開發審計、源代碼管理、依賴管理、風險評估和監控、漏洞管理和修補等方面的工作。只有確保軟體供應鏈的安全性,才能保護軟體系統免受潛在的攻擊和風險。

如果套件的其中一包有問題 就會導致整個系統有問題, 如下圖

How_Go_solve_security_problems

Code
VS Code IDE Plugin
- Go:官方提供的Golang插件,提供了語法高亮、代碼完成、導入管理、代碼格式化等功能。
- Go Test Explorer:可以在VS Code中運行和管理Golang測試,提供了方便的測試運行和結果查看。
- GoLint:用於檢查和提供Go程式碼中的錯誤、風格和規範建議的Lint工具。
- GoImports:用於自動處理Go程式碼中的導入項目,可以自動添加和刪除導入語句,保持代碼整潔。
- GoDoc:用於在VS Code中查看Golang文檔,可以方便地瀏覽和查詢庫和函數的文檔。
- Gopls:官方提供的Language Server Protocol(LSP)插件,提供了強大的代碼分析和建議功能,可以進行智能代碼完成、重構、跳轉定義等操作。
- Delve:用於Golang調試的工具,可以在VS Code中啟動和管理調試會話,提供了逐步執行、設置斷點、查看變量等功能。
Dependency Management
在Golang中,可以使用以下幾種方式進行依賴管理:
- Go Modules:Go Modules是Go語言官方引入的依賴管理系統。它允許在專案中聲明和管理依賴關係,並且能夠自動解析和下載相應的依賴模塊。使用Go Modules可以確保專案的依賴關係的版本一致性,並且方便地進行依賴的更新和管理。
- GOPATH 和 go get:在較舊的Golang版本中,使用者可以將專案代碼放在GOPATH目錄中,然後使用"go get"命令從版本控制系統(如Git)中下載依賴。這種方式適用於較簡單的專案或個人開發,但對於較大型的專案或團隊開發,建議使用Go Modules。
- 第三方工具:還有一些第三方工具可以用於Golang的依賴管理,例如Glide、Dep等。這些工具提供了額外的功能,例如依賴解析、版本管理、依賴鏈可視化等。然而,隨著Go Modules的引入,官方已經提供了強大的依賴管理功能,因此建議優先考慮使用Go Modules。
Build
Delve Debugger
Delve Debugger 是一個用於 Go 語言的強大、靈活的調試器工具。它提供了許多調試功能,可以幫助開發人員追蹤和修復 Go 程式碼中的錯誤。Delve 可以與各種開發環境和 IDE 配合使用,例如 Visual Studio Code、GoLand、Emacs 等等,並且支援各種平台,包括 Windows、Linux 和 macOS。
Delve 提供了許多強大的功能,包括斷點設置、變數查看、堆棧追蹤、單步執行、追蹤 goroutine、修改變數值等等。它還支援遠端調試,可以在遠程服務器上調試運行中的 Go 程式碼。 Delve 是 Go 社區中廣泛使用的調試器工具,可以有效地幫助開發人員進行 Go 語言程式碼的調試和問題排查。
安裝 Delve
# 安裝最新穩定版本:
go install github.com/go-delve/delve/cmd/dlv@latest
# 安裝最新開發中版本:
$ go install github.com/go-delve/delve/cmd/dlv@master
使用 Delve
dlv help # 顯示dlv幫助信息
dlv version # 顯示dlv版本信息
dlv attach # 連接到正在運行的go進程並開始調試
dlv connect # 連接到調試服務器,用於遠程調試,需要遠程有二進製文件和源碼及dlv工具
dlv core
dlv dap
dlv debug # 默認在當前目錄中編譯並開始調試go main 包,或者指定模塊路徑
dlv exec # 執行已經編譯好的二進製文件,並開始調試
dlv run # 不推薦使用,用debug代替
dlv test # 編譯測試二進製文件,並開始調試,即調試測試文件
dlv trace # 編譯並開始追踪程序
# debug 和 exec是比較常用的命令
Sample code
package main
import (
"fmt"
)
func demo(s string, x int) string {
ret := fmt.Sprintf("This is a demo, your input is: %s %d", s, x)
return ret
}
func main() {
s := "string"
i := 1111
fmt.Println(demo(s, i))
}
開始用 delve dubug
dlv debug delve-demo.go
可以看到下面訊息
$ dlv debug delve-demo.go
Type 'help' for list of commands.
(dlv)
但其實程式還沒真的跑起來,此時可以先設定中斷點,再來跑程式。
設定中斷點:
使用
# 方法 1
(dlv) break main.main
Breakpoint 1 set at 0x10b0958 for main.main() ./delve-demo.go:12
# 方法 2
(dlv) break delve-demo.go:7
Breakpoint 2 set at 0x10b0758 for main.demo() ./delve-demo.go:7
接著使用 c 或是 continue讓程式跑起來,你就會看到 dlv 停在中斷點上:

常用指令
- 單部執行: n 或 next
- 繼續執行: c 或 continue
- 跳進去函式 (step in): s 或 step
- 跳出函式 (step out): stepout
- 看函式引數: args
- 印出參數或表達式: p 或 print <參數>
- 印出目前所有的 goroutine:goroutines
運行命令
continue # 運行到斷點或程序結束,簡寫 c
next # 跳到下一個源碼行,簡寫 n
rebuild # 重新編譯目標文件並執行,如果可執行文件不是通過dlv編譯的就不起作用
restart # 重啟程序
step # 單步執行程序,遇到函數進入,簡寫 s
step-instruction # 單cpu指令執行,簡寫 si
step-out # 跳出當前函數,簡寫 so
操作斷點
break # 打斷點,簡寫 b
breakpoints # 查看斷點列表,簡寫 bp
clear # 刪除斷點
clearall # 刪除所有斷點
condition # 設置斷點條件
on # 在命中斷點時執行
trace # 設置追踪點
查看程序變量和內存
args # 打印函數變量
locals # 打印局部變量
print # 打印變量的值,如print a
regs # 打印cpu 寄存器內容
set # 設置變量的值,如 set a = 100
vars # 打印包變量
whatis # 查看變量類型,如 whatis a,輸出 int
列出和選擇線程或協程
goroutines # 查看所有協程
goroutines {number} # 選擇指定協程,如 goroutines 1
threads # 列出所有線程
thread # 選擇指定線程,如 thread 2208678
查看調用堆棧並選擇幀
其他命令
funcs # 列出函數列表
exit # 退出程序
help # 幫助信息
list # 顯示源代碼
sources # 打印源碼文件列表
types # 打印類型列表
詳細指令說明 : https://github.com/derekparker/delve/blob/master/Documentation/cli/README.md
VS Code debug with delve
Create Debug create a launch.json file.

mode 設置為debug時,program的內容${fileDirname}即可, mode 設置為exec時,program的值為二進製文件的路徑,通過設置mode的值,即可調試源碼和二進製程序(也需要有源碼)。 mode模式為auto時,測試了下,vscode 並不能通過program的內容來判斷是debug還是exec.
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}"
}
]
}
遠程Debug
dlv debug --headless --listen=:2345 --api-version=2 --accept-multiclient
dlv exec --headless --listen=:8989 ./test --api-version=2 --accept-multiclient
修改 launch.json
{ "version": "0.2.0", "configurations": [ { "name": "Remote Debug", "type": "go", "request": "attach", "mode": "remote", "remotePath": "${workspaceFolder}", "port": 2345, "host": "127.0.0.1" } ] }在遠端執行dlv命令
dlv debug --headless --listen=:2345 --api-version=2 --accept-multiclient #用degbug方式啟動遠程應用程序
dlv exec --headless --listen=:2345 ./test --api-version=2 --accept-multiclient # exec執行當前目錄下的test二進製文件
--listen:指定調試端口 --api-version:指定api版本,默認是1 --accept-multiclient:接受多個client調試
結果如下:
$ dlv debug --headless --listen=:2345 --api-version=2 --accept-multiclient
API server listening at: [::]:2345
2023-05-18T17:53:33+08:00 warning layer=rpc Listening for remote connections (connections are not authenticated nor encrypted)
debugserver-@(#)PROGRAM:LLDB PROJECT:lldb-1403.0.17.64
for x86_64.
Got a connection, launched process /Users/kimi/go/src/Build_more_secure_apps_with_Go_and_Google/example/__debug_bin (pid = 45801).
This is a demo, your input is: string 1111
Exiting.
from : https://www.jianshu.com/p/4f3c2e913915
Souce Code Analysis
在Golang中,可以使用各種工具和方法進行源代碼分析(source code analysis)。 以下是一些常用的Golang源代碼分析工具和技術:
1. 靜態代碼分析工具 (Static Code Analysis Tools)
這些工具可用於檢查代碼中的潛在錯誤、漏洞和不良實踐。內建的 go vet。通常會檢查代碼中的語法錯誤、未使用的變量、錯誤的代碼風格等。
2. 代碼質量工具 (Code Quality Tools)
這些工具可用於評估代碼的質量和可讀性。例如,Golangci-lint是一個綜合性的代碼質量工具,可以檢查代碼中的各種問題,如錯誤處理、性能問題、代碼風格等。 如果要在vscode配置的話, 參考: golangci-lint在vscode的使用,以及配置的一些探索
3. 安全漏洞掃描工具(Security Vulnerability Scanning Tools)
這些工具專注於檢測代碼中的安全漏洞。例如,GoSec是一個用於Golang代碼的安全漏洞掃描工具,可以檢測常見的安全問題,如潛在的XSS漏洞、SQL注入漏洞等。
4. 測試覆蓋率工具(Test Coverage Tools)
這些工具用於評估測試的覆蓋率,即測試用例對代碼的涵蓋程度。 Go 有內建此功能: https://go.dev/blog/cover . 執行方式如下:
- 在命令列中執行以下指令,以執行測試並輸出覆蓋率檔案
go test -coverprofile=coverage.out. 這將執行測試並將測試覆蓋率的結果輸出到 coverage.out 檔案中。 - 使用以下指令來生成 HTML 格式的覆蓋率報告
go tool cover -html=coverage.out -o coverage.html. 這將生成一個名為 coverage.html 的 HTML 檔案。你可以在瀏覽器中打開它,查看詳細的覆蓋率報告。
main.go
package coverdemo
import (
"fmt"
)
// 假設這是一個要進行測試的函式
func Add(a, b int) int {
return a + b
}
main_test.go
package coverdemo
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
expected := 5
if result != expected {
t.Errorf("Expected %d, but got %d", expected, result)
}
}
執行結果
$ go test -coverprofile=coverage.out
PASS
Build_more_secure_apps_with_Go_and_Google/example/cover-demo coverage: 100.0% of statements
ok Build_more_secure_apps_with_Go_and_Google/example/cover-demo 0.018s
此外,也可以使用一些集成開發環境(IDE)或代碼編輯器的擴展插件,如Visual Studio Code的Go插件或Goland IDE,這些插件通常提供代碼分析和檢測功能,可以在開發過程中即時提供有關代碼質量和潛在問題的反饋。
總結而言,Golang的源代碼分析可以通過靜態代碼分析工具、代碼質量工具、安全漏洞掃描工具和測試覆蓋率工具等進行。這些工具可以幫助開發人員發現潛在的錯誤、漏洞和代碼質量問題,提高代碼的可靠性和安全性。
SBOM
SBOM (Software Bill of Materials) generation 是指生成軟體成分清單的過程。軟體成分清單是一份清單,其中包含了軟體項目所使用的所有元件、依賴庫和其他第三方軟體的詳細資訊。這些資訊可以包括軟體元件的版本、許可證、來源、漏洞等。SBOM 的生成通常是透過自動化工具或流程,從軟體項目的原始碼、依賴管理系統或軟體註冊表中提取相關資訊。生成 SBOM 可以幫助組織更好地管理和監控軟體供應鏈安全性,以及應對潛在的風險和漏洞。
在 Golang 中,您可以使用一些工具來生成軟體供應鏈資訊 (Software Bill of Materials, SBOM)。SBOM 是一份清單,列出了應用程式所使用的開源元件及其相關資訊,包括版本號、授權條款等。以下是一些常用的 Golang 工具和方法:
- Go Modules: 如果您使用 Go Modules 來管理依賴關係,您可以使用 go mod 指令來生成相關資訊。執行以下指令:
go mod vendor
這將生成一個 vendor 目錄,其中包含應用程式所使用的所有相依套件。然後,您可以掃描 vendor 目錄並擷取相關資訊以生成 SBOM。
Go Package License: go-license 是一個用於解析 Golang 專案中套件授權條款的工具。您可以使用以下指令安裝:
go install github.com/google/go-licenses@latest
然後,您可以運行以下命令來生成 SBOM:
go-licenses csv . > sbom.csv
這將將 SBOM 寫入 sbom.csv 檔案中,其中包含每個套件的名稱、版本和授權條款。
請注意,以上僅為一些常用的工具和方法,供您參考。根據您的專案需求和套件管理方式,可能會有其他選擇和工具可供使用。請根據您的情況選擇最適合的方法來生成 SBOM。
Test
Build-in Test Framework
test-demo.go
func Pow(base uint, exponent uint) uint {
var result uint = 1
if base == 0 {
return result
}
var i uint
for i = 0; i < exponent; i++ {
result *= base
}
return result
}
test-demo_test.go
func TestPow(t *testing.T) {
var tests = []struct {
arg1 uint
arg2 uint
want uint
}{
{
2,
3,
8,
},
}
for _, tt := range tests {
if got := Pow(tt.arg1, tt.arg2); got != tt.want {
t.Errorf("got = %v, want = %v", got, tt.want)
}
}
}
執行 go test -timeout 30s -run test-demo -v -count=1
#go test -timeout 30s -run test-demo -v -count=1
=== RUN TestPow
--- PASS: TestPow (0.00s)
PASS
ok Build_more_secure_apps_with_Go_and_Google/example/test-demo 0.011s
Build-in Fuzz Testing
Fuzz testing(模糊測試)是一種軟體測試方法,旨在發現軟體應用程式中的程式錯誤、安全漏洞和異常行為。它通常是自動化的測試技術,使用大量的隨機、無效或不正常的輸入來觸發程式中的錯誤情況。
在模糊測試中,測試工具會生成具有隨機或不正常特徵的輸入,然後將其提供給被測試的軟體應用程式。測試工具會監控應用程式的行為,例如記錄異常終止、崩潰、記憶體洩漏或安全漏洞。這種測試方法可以揭示潛在的程式錯誤、緩衝區溢位、拒絕服務攻擊等問題,幫助開發者在軟體發佈之前發現並修復這些問題。
模糊測試是一種廣泛應用於軟體開發和安全測試領域的技術,可以提高軟體的可靠性和安全性。它可以幫助開發者發現並修復潛在的錯誤和漏洞,並減少軟體在生產環境中出現問題的風險。
func FuzzPow(f *testing.F) {
f.Fuzz(func(t *testing.T, arg1, arg2 uint) {
assert := assert.New(t)
assert.Equal(Pow(arg1, 0), uint(1))
assert.Equal(Pow(arg1, 1), arg1)
assert.Equal(Pow(arg1, 2), arg1*arg1)
if arg1 > 0 && arg2 > 0 { // 這邊會出錯!!!
assert.Equal(Pow(arg1, arg2)/arg1, Pow(arg1, arg2-1))
}
}

此時執行 go test -fuzz=FuzzPow -run FuzzPow -count=1 -fuzztime=10s , 會出現錯誤如下
go test -fuzz=FuzzPow -run FuzzPow -count=1 -fuzztime=10s
fuzz: elapsed: 0s, gathering baseline coverage: 0/11 completed
failure while testing seed corpus entry: FuzzPow/0f39546f892569a0
fuzz: elapsed: 0s, gathering baseline coverage: 0/11 completed
--- FAIL: FuzzPow (0.07s)
--- FAIL: FuzzPow (0.00s)
test-demo_test.go:71:
Error Trace: /Users/kimi/go/src/Build_more_secure_apps_with_Go_and_Google/example/test-demo/test-demo_test.go:71
/usr/local/Cellar/go/1.20.4/libexec/src/reflect/value.go:586
/usr/local/Cellar/go/1.20.4/libexec/src/reflect/value.go:370
/usr/local/Cellar/go/1.20.4/libexec/src/testing/fuzz.go:335
Error: Not equal:
expected: 0x247291bf804951a
actual : 0xe4e6a2d06d86ae49
Test: FuzzPow
FAIL
exit status 1
FAIL Build_more_secure_apps_with_Go_and_Google/example/test-demo 0.088s
在做 Fuzzing Test 的時候如果跑一跑 FAIL 了,Go 會幫忙把那組 input 記在 testcase/ 裡面,所以先別急著下定論,我們來看一下到底是怎麼樣的 x 跟 y 會讓這個 assert 失敗。
長得像這樣 test-demo/testdata/fuzz/FuzzPow/0f39546f892569a0
go test fuzz v1
uint(61)
uint(51)
意思是發生在 arg1=61、arg2=51 時 assert 會失敗,也就是說 pow(61, 50)/61 不會等於 pow(61, 50)
因為 Go 定義的 max.MaxUint : MaxUint = 1<<intSize - 1 // MaxUint32 or MaxUint64 depending on intSize.
MaxUint 的具體數值取決於不同的系統架構和編譯器實現。在大多數系統中,MaxUint 的值通常是 2 的 N 次方減 1,其中 N 是 uint 數據類型的位數。 例如,對於 64 位系統,MaxUint 的值為 18446744073709551615, 大約是 $18 \times 10^{18}$。
$61^{51}$ = 1.126734482271601e91 這個數字大約是 $1.126734482271601 \times 10^{91}$,遠遠超過了 uint64 的最大值,所以 Go 會把它轉換成 uint64 的最大值 18446744073709551615。 再除以61 = $18446744073709551615\div61$
$61^{50}$ = 1.847105708641968e89 這個數字大約是 $1.847105708641968 \times 10^{89}$,遠遠超過了 uint64 的最大值,所以 Go 會把它轉換成 uint64 的最大值 18446744073709551615。
所以 $61^{51}\div61$,就會跟 $61^{50}$ 不一樣
這就是 Fuzzing 幫我們找到的 edge case,也是我們當初沒想到的。
Improved Pow
仔細想想,pow(x, y) 在計算的過程中會不會發生 overflow 完全取決於使用者的輸入是多少。因此我們不可能避免 overflow,但至少我們能在 overflow 發生時讓使用者知道,而不是已經乘到 overflow 還繼續裝沒事,這樣可能有天會導致我們想都沒想過的 bug。
依循這個想法,我們可以幫 pow 多加一個 error type 的回傳值,如果在計算的過程中不幸發生 overflow,就可以回傳 ErrOverflow,讓使用者自己決定要怎麼處理。
func ImprovedPow(base uint, exponent uint) (uint, error) {
var result uint = 1
if base == 0 {
return result, nil
}
var i uint
for i = 0; i < exponent; i++ {
result *= base
}
if math.MaxUint64/base < result {
return 0, ErrorOverflow
}
return result, nil
}
// go test -fuzz=FuzzImprovedPow -run FuzzImprovedPow -count=1 -fuzztime=10s
func FuzzImprovedPow(f *testing.F) {
f.Fuzz(func(t *testing.T, arg1, arg2 uint) {
assert := assert.New(t)
if result, err := ImprovedPow(arg1, 0); err != ErrorOverflow {
assert.Equal(result, uint(1))
}
if result, err := ImprovedPow(arg1, arg2); arg1 > 0 && arg2 > 0 && err != ErrorOverflow {
if resultDivX, errDivX := ImprovedPow(arg1, arg2-1); errDivX != ErrorOverflow {
assert.Equal(result/arg1, resultDivX)
}
}
})
}
再執行 go test -fuzz=FuzzImprovedPow -run FuzzImprovedPow -count=1 -fuzztime=10s
這樣就不會出現錯誤了~
go test -fuzz=FuzzImprovedPow -run FuzzImprovedPow -count=1 -fuzztime=10s
fuzz: elapsed: 0s, gathering baseline coverage: 0/14 completed
fuzz: elapsed: 0s, gathering baseline coverage: 14/14 completed, now fuzzing with 8 workers
fuzz: elapsed: 3s, execs: 97648 (32549/sec), new interesting: 0 (total: 14)
fuzz: elapsed: 6s, execs: 207161 (36504/sec), new interesting: 0 (total: 14)
fuzz: elapsed: 9s, execs: 315984 (36273/sec), new interesting: 0 (total: 14)
fuzz: elapsed: 10s, execs: 353105 (33685/sec), new interesting: 0 (total: 14)
PASS
ok Build_more_secure_apps_with_Go_and_Google/example/test-demo 10.115s
Release
Static Binaries
-ldflags
Go的-ldflags標誌,指定-extldflags "-static"選項,以告訴連接器使用靜態連接庫
- 使用 -X 標誌將變數的值設置為指定的版本信息。
flags="-X main.version=0.1.0 -X main.goversion=$(go version) -X main.buildstamp=`date -u '+%Y-%m-%d_%I:%M:%S%p'` -X main.githash=`git describe --long --dirty --abbrev=14`"
go build -ldflags "$flags" -x -o build-version main.go
如果要傳入檔案的方法:
go build -ldflags "-X 'package/path.variableName=variableValue'"
# example:
go build -ldflags "-X 'github.com/example/myapp.version=1.0.0'"
當你運行 git describe --always 命令時,Git 會輸出一個代表當前提交的字符串,格式如下:
- git describe --always
- Git 會輸出一個代表當前提交的字符串,格式如下:
<最接近的標籤>-<提交數量>-g<簡略提交哈希>- <最接近的標籤>:從當前提交可達的最近的標籤。如果沒有標籤,則輸出為空字符串。
- <提交數量>:最接近的標籤和當前提交之間的提交數量。
- g:指示它是一個基於 Git 的版本字符串的前綴。
- <簡略提交哈希>:當前提交的簡略哈希值。
- --always,即使提交沒有相應的標籤,你仍然可以獲得一個用於識別提交的唯一標識, 這在某些情況下非常有用,可以用於跟踪和引用提交
- Git 會輸出一個代表當前提交的字符串,格式如下:
- --long:這個標誌用於在版本信息中包含完整的 Git 提交哈希(commit hash)。在使用這個標誌時,版本信息將包含完整的長度的提交哈希。
- --dirty:這個標誌用於在版本信息中標記程式碼的狀態是否為 "dirty"(未提交)。如果程式碼存在未提交的更改,則在版本信息中會添加 "-dirty" 字串。
- --abbrev=14:這個標誌用於設置提交哈希的簡寫長度。在這個例子中,提交哈希將被縮短為 14 個字符。
- 使用 -Wl 標誌將連結器選項直接傳遞給連結器。例如,-ldflags "-Wl,-s" 將 -s 選項傳遞給連結器,用於壓縮二進制文件。
-v就可以看到版本資訊
$./build/linebot-go.mac.x64 version
Git Commit Hash: cecc3cf4c7691e-dirty
UTC Build Time : 2023-05-19_09:06:15AM
Golang Version : go version go1.20.4 darwin/amd64
flags="-X main.version=0.1.0 -X 'linebot-go/cmd.goversion=$(go version)' -X linebot-go/cmd.buildstamp=`date -u '+%Y-%m-%d_%I:%M:%S%p'` -X linebot-go/cmd.githash=`git describe --always --long --dirty --abbrev=14`"
echo "$flags"
# # build current
go build -ldflags "$flags" -o build/linebot-go -o build/linebot-go
# build for Linux
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$flags" -o build/linebot-go.linux.x64 main.go
# build for Windows
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "$flags" -o build/linebot-go.windows.x64.exe main.go
# build for Mac (intel)
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "$flags" -o build/linebot-go.mac.x64 main.go
# build for Mac (apple)
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags "$flags" -o build/linebot-go.mac.arm64 main.go
Memory Safe
在Go語言中,有一些設計和特性可以幫助開發人員實現內存安全:
- 垃圾回收(Garbage Collection):Go語言使用自動垃圾回收機制,負責自動回收不再使用的內存,從而減輕了開發人員對內存管理的負擔。
- 強類型和編譯時類型檢查:Go語言是一種靜態類型語言,它在編譯時對類型進行檢查,這有助於減少類型相關的錯誤,例如對不匹配的類型進行內存操作。
- 切片(Slice)和映射(Map)的安全訪問:Go語言提供了切片和映射等集合類型,並且在訪問元素時進行邊界檢查,從而防止訪問超出範圍的內存。
- 禁止指標算術操作:Go語言中禁止了指標的算術操作,這有助於防止指標越界或對無效指標進行操作的情況發生。
- 安全的底層操作:Go語言提供了安全的底層操作機制,例如使用unsafe包來進行低級別的內存操作,並通過嚴格的規則和限制確保操作的安全性。
Compatibility Promise
Golang(Go)遵循一項稱為「Go 1相容性承諾」或「Go 1相容性保證」的相容性承諾。 這項承諾確保一旦您使用特定的Go 1.x版本編寫和編譯代碼,它將繼續在未來的Go 1.y版本(其中y > x)中正確編譯和運行,而無需進行任何修改或代碼更改。 Go團隊致力於在Go 1的主要版本中維護向後相容性。這意味著使用Go 1.x編寫的程序應該能夠在未來的Go 1.y版本中正常工作,前提是該代碼符合語言規範並且不依賴於任何已棄用的功能。
這個相容性承諾確保您可以放心地編寫和維護Go代碼,知道它將與同一主要版本的未來Go版本相容。 然而,需要注意的是,這個承諾只適用於標準庫和核心語言特性。它不包括第三方庫或包,因為它們的相容性可能因其維護和版本控制方式而有所不同。
Monitor
Vulnerability Monitoring

- https://deps.dev/ : Open Source Insights is a service developed and hosted by Google to help developers better understand the structure, construction, and security of open source software packages

- https://github.com/golang/vuln : Contains the database client and tools for the Go vulnerability database.
使用 govulncheck,您可以輕鬆地檢測您的 Go 應用程式所使用的依賴庫是否有已知的安全漏洞。它會解析您的 Go 模組文件(go.mod),並對每個依賴庫進行漏洞掃描。如果發現有受影響的依賴庫,govulncheck 會提供相關的漏洞資訊,讓您可以及時更新依賴庫版本或應用相應的安全修補程式。
Install govulncheck
go install golang.org/x/vuln/cmd/govulncheck@latest
執行方式 govulncheck .

Operate
Vulnerability Scanning
「Vulnerability Scanning」(漏洞掃描)是一種用於檢測和識別應用程式中潛在漏洞和安全風險的技術。 這個過程通常涉及使用自動化工具來分析應用程式的代碼、依賴庫和相關組件,並檢查它們是否存在已知的安全漏洞或弱點。
通常包含以下步驟:
- 選擇漏洞掃描工具:首先,選擇一個適合的漏洞掃描工具。有許多商業和開源的漏洞掃描工具可供選擇,例如OWASP ZAP、Nessus、Qualys、Snyk等。請確保選擇的工具支援Golang程式碼的掃描和依賴庫的漏洞檢測。
- OWASP Dependency-Check:用於檢測依賴庫中已知的安全漏洞。
- Snyk:提供漏洞掃描和監控服務,用於檢測和修補依賴庫中的漏洞。
- SonarQube:一個開源的代碼質量和安全性平台,可用於進行靜態程式碼分析和檢測安全漏洞。
- CodeQL:由GitHub開發的靜態程式碼分析工具,用於識別並檢測漏洞。
- Checkmarx:提供靜態程式碼分析和漏洞掃描服務,用於檢測安全漏洞。
- 安裝和設定工具:根據選擇的漏洞掃描工具的官方文件或指南,進行安裝和設定。確保按照指示配置掃描工具,以便它能夠正確分析和掃描您的Golang應用程式。
- 掃描應用程式:運行漏洞掃描工具,指定要掃描的Golang應用程式的目錄或程式碼位置。工具將分析您的程式碼,檢查常見的安全漏洞和弱點。同時,它還會掃描相關的依賴庫,以檢測是否存在已知的漏洞。
- 分析結果:完成掃描後,漏洞掃描工具將生成一份報告,詳細列出掃描期間檢測到的漏洞和問題。該報告通常包含漏洞的嚴重程度、影響範圍和修復建議。仔細分析報告並優先解決高風險的漏洞。
- 漏洞修復和改進:根據漏洞報告中的建議,開發團隊應優先處理並修復檢測到的漏洞。這可能包括修補程式碼、升級依賴庫版本或實施其他安全措施。同時,對於掃描期間未檢測到的潛在漏洞,也應該根據最佳實踐進行改進。
- 定期重複掃描:漏洞掃描應該成為持續的安全實踐,而不僅僅是一次性的。定期重複進行漏洞掃描,以確保應用程式的安全性並及時檢測新的漏洞。可以設定自動化工具或服務來定期執行掃描,或將其整合到持續集成/持續交付(CI/CD)流程中。
Dynamic Data Race Detector
將-race標誌添加到go build、go run或go test命令中
go run -race main.go
Data Race Detector僅在運行時檢測並報告數據競爭問題。 因此,它對於單元測試和測試覆蓋範圍不足的情況非常有用。 建議在開發和測試過程中經常使用它,以確保代碼的並發安全性。 不過會對程序的性能產生一定的影響,因此它在生產環境中並不適用
Other Language

Suite of tools for open source software security

- https://docs.deps.dev/
- OpenSSF Scorecard : https://securityscorecards.dev/
- OSV.dev : https://osv.dev/
Reference
- https://www.youtube.com/watch?v=HSt6FhsPT8c
- https://go.dev/blog/vuln
- github.com/go-delve/delve
- github.com/securego/gosec
- The Go Blog : The cover story
- golangci-lint在vscode的使用,以及配置的一些探索
- github.com/google/go-licenses
- 善用 Go Fuzzing,幫助你寫出更完整的單元測試
- Vulnerability Management for Go
- Go Vulnerability Management
- Golang 單步除錯利器 — Delve
- Markdown 数学公式以及符号整理