Go 🐿 Application Security and AppSec Automation Made Easy

Photo by David Thielen on Unsplash

Prerequisites

$ go versiongo version go1.19.1 darwin/amd64

Security Scanners

By running security scanners before pushing code, we can detect and remediate vulnerabilities before we deploy our code to a production-level environment. I will go over how to use a variety of different security scanners for Go such as GoSec, GoVulnCheck, and Fuzz.

# Set the appropriate GOPATH
$ export GOPATH=/path/to/your/go/projects
# Add your GOPATH bin directory to your PATH
$ export PATH=$PATH:$GOPATH/bin
# Go into your GOPATH
$ cd $GOPATH
# Create the proper directory structure
$ mkdir -p src/gitlab.com/awkwardferny
# Clone application which we will be scanning
$ git clone git@gitlab.com:awkwardferny/insecure-microservice.git src/gitlab.com/awkwardferny/insecure-microservice
# Go into the application root
$ cd src/gitlab.com/awkwardferny/insecure-microservice

GoSec (Source Code Analysis)

The first security scanner we will cover is GoSec. It is a popular Go security scanner which scans your application’s source code and dependencies for vulnerabilities. It works by pattern matching your source code against a set of rules.

GoSec mascot 🚓
# Install GoSec
$ go install github.com/securego/gosec/v2/cmd/gosec@latest
# Run GoSec
$ gosec ./...
G404 (CWE-338): Use of weak random number generator (math/rand instead of crypto/rand) (Confidence: MEDIUM, Severity: HIGH)G114 (CWE): Use of net/http serve function that has no support for setting timeouts (Confidence: HIGH, Severity: MEDIUM)G104 (CWE-703): Errors unhandled. (Confidence: HIGH, Severity: LOW)G104 (CWE-703): Errors unhandled. (Confidence: HIGH, Severity: LOW)G104 (CWE-703): Errors unhandled. (Confidence: HIGH, Severity: LOW)G104 (CWE-703): Errors unhandled. (Confidence: HIGH, Severity: LOW)

Govulncheck (Source Code Analysis)

Next up is Govulncheck! Govulncheck is a security scanner for source code and application dependencies. It is under active development by the Go security team and is different than GoSec in a few ways:

Govulncheck architecture diagram
# Install govulncheck
$ go install golang.org/x/vuln/cmd/govulncheck@latest
# Run govulncheck
$ govulncheck ./...
Vulnerability #1: GO-2020-0016An attacker can construct a series of bytes such that calling Reader. Read on the bytes could cause an infinite loop. If
parsing user supplied input, this may be used as a denial of service vector.
Call stacks in your code:internal/logic/logic.go:63:8: gitlab.com/awkwardferny/insecure-microservice/internal/logic.insecure calls github.com/ulikunitz/xz.Reader.ReadFound in: github.com/ulikunitz/xz@v0.5.7
Fixed in: github.com/ulikunitz/xz@v0.5.8
More info: https://pkg.go.dev/vuln/GO-2020-0016

Fuzz (Fuzz-Testing)

And last we are going to go over fuzz testing. Fuzz testing is the practice of inputing random/malformed data into an application in an attempt to reveal security issues or bugs. Go has a native fuzzing library called fuzz.

func FuzzAdd(f *testing.F) {
f.Add("1", "2")
f.Fuzz(func(t *testing.T, a string, b string) {
result, err := add(a, b)
if err != nil {
t.Errorf(fmt.Sprintf("error: %v", err))
}
intA, _ := strconv.Atoi(a)
intB, _ := strconv.Atoi(b)
expected := intA + intB
if result != expected {
t.Errorf(fmt.Sprintf("expected %v, got %v", expected, result))
}
})
}
func add(a string, b string) (c int, e error) {
intA, err := strconv.Atoi(a)
if err != nil {
return 0, nil
}
intB, err := strconv.Atoi(b)
if err != nil {
return 0, nil
}
return (intA + intB), nil
}
# Run the fuzz tests
$ go test ./internal/logic -fuzz FuzzAdd
--- FAIL: FuzzAdd (0.10s)
--- FAIL: FuzzAdd (0.00s)
logic_test.go:44: expected 1, got 0

Failing input written to testdata/fuzz/FuzzAdd/9f4dc959af0a73c061c4b4185e9fdb9e5dbfc854cccce7bf9199f0f5556c42a9
To re-run:
go test -run=FuzzAdd/9f4dc959af0a73c061c4b4185e9fdb9e5dbfc854cccce7bf9199f0f5556c42a9
FAIL
func add(a string, b string) (c int, e error) {
intA, err := strconv.Atoi(a)
if err != nil {
// change value to 0 if error is thrown
intA = 0
// TODO: Log the error (err)
}
intB, err := strconv.Atoi(b)
if err != nil {
// change value to 0 if error is thrown
intB = 0
// TODO: Log the error (err)
}
return (intA + intB), nil
}

Automation of Scanners with GitLab

Running the security scanners to search for vulnerabilities in your Go application can be automated so that we can run the scanners on a feature branch each time code is pushed.

Photo by Minku Kang on Unsplash
stages:
- build
- test
build:
image: golang:alpine
stage: build
script:
- go mod download
- go build .
unit:
image: golang:alpine
stage: test
script:
- apk update
- apk add g++ git
- go test -v ./...

gosec:
image: golang:alpine
stage: test
script:
- apk update
- apk add g++ git
- go install github.com/securego/gosec/v2/cmd/gosec@latest
- gosec ./...

go-vuln-check:
image: golang:alpine
stage: test
script:
- apk update
- apk add g++ git
- go install golang.org/x/vuln/cmd/govulncheck@latest
- govulncheck ./...

fuzz:
image: golang:alpine
stage: test
script:
- apk update
- apk add g++ git
- go test ./internal/logic -fuzz FuzzAdd -fuzztime 50s
artifacts:
paths:
- internal/logic/testdata/*
when: on_failure
Insecure microservice pipeline running in GitLab
Govulncheck job ouput

Code Reviews and Secure Coding Practices

Last, but not least, in order to enhance application security, you should always perform code reviews. This is crucial because others can find issues that you may miss. Scanners may find vulnerabilities, but they cannot detect incorrect logic.

Photo by charlesdeluvio on Unsplash

Other Considerations

Separation of duties

Another way to reduce insecure code from making it to production is to enforce separation of duties. Separation of duties is the concept where developers should only have access to the functions which are necessary for their job. Some examples of this would be:

  • Don’t allow developers to merge their own commits
  • Require security team or team-lead approval if a vulnerability is found
  • Don’t allow security scans to be disabled by developers
  • Implement CODEOWNERS functionality

Other attack vectors

There are other aspects of an application which can be susceptible to attack which are not part of the application source code. Some examples of this include:

  • Container images
  • Application dependencies in other languages
  • Restrictive licenses
  • Configurations within the running application/server

Visibility into security posture

Another thing to consider is how great your visibility into your application’s security posture is. You should have insight on which projects have the most concerning vulnerabilities and what is being done about them.

Photo by Avrielle Suleiman on Unsplash

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Fernando Diaz

Fernando Diaz

Senior Technical Marketing @ GitLab 🦊, Developer 👨🏻‍💻, Alien 👽