Golang Security

Galah Fuzzing

This is the first of a series of blog posts that cover language-specific security concerns. I’ve been frustrated when searching for language-specific details. Most content is shallow and likely content marketing for tool sales.

“Validate Inputs” and “Patch your dependencies!” are common themes and while they are good general tips, I want to go deeper.

What are the security implications of choosing a language? What features exist in a language to help engineers build secure software? Are there any footguns?

I want to know the threat model behind the language design and what best practices I should be using! So I’ve decided to write that content, and start with my favourite language, which is go!

Galah Inception
I’ve been programming with it on and off for 5 years now. While I’m not a great developer, I’ve gotten used to a lot of the features and quirks of the language. I wanted to share the ones that I think have the most value for security professionals and engineers to consider when building go applications.

Use the Defer keyword

A defer statement defers the execution of a function until the surrounding function returns. For example source: https://go.dev/tour/flowcontrol/12

import "fmt"
func main() {
     defer fmt.Println("world")
     fmt.Println("hello")
}

This prints “hello\nworld\n”. When the main method finishes execution, the defer statement is called in Last-in, First-out (LIFO) order.

Defer mimics the Finally() clause often found in error handling w/ try {} catch {} clauses. While Try/Catch/Finally is boilerplate-y and there are better ways of handling errors, I find defer to be clean and neat way of handling a number of security-related events with go programs.

These can include:

  • Resource Management: You can use a defer statement immediately after opening a resource to make sure you close the resource when that function scope exits. I like this as it means you can group resource management functionality together. This is a clean and intuitive way to show other engineers that you are safely handling files, database connections, and more.
  • Privilege Management: Sometimes you need to elevate your privileges to interact w/ the underlying host or a cloud service. We want to make sure our programs don’t perpetually have elevated access, so using a defer statement makes sure we can revoke our privilege when our function completes execution.
  • Logging: If a sensitive action like create-user or raising privileges is performed, you would want to record that event. Adversaries may find ways to cause an application to panic mid-execution, which can cause the app to not log these events. But defer calls will still execute before a go application panics and shuts down. This guarantees that you are safely logging events, regardless of control flow manipulation.
  • Preventing race conditions: Go was written with measures to prevent race conditions (Channels + goroutines). But this is in the context of multi-threading. So using defer to lock and unlock shared resources is another way to easily prevent bugs from occurring.

I like the defer statement. Even if you only use it for resources, you’re eliminating a bug class in a clean and concise way.

Discarding error messages with the blank identifier

The blank identifier (Underscore _ ) operator is used to ignore values returned by functions. The go compiler can be pretty obnoxious. It frequently complains about unused packages, variables, and functions. This is especially annoying early in software development as you try to plan out your architecture, variables, and packages you plan to use.

The blank identifier can help programmers get around this by telling the compiler to chill out.

import _ "fmt"
import _ "strconv"
import _ "strings"

Fmt, strings, and stconv are used in a large quantity of go apps, so having to add useless statements like fmt.println() to turn off the compiler warnings slows development velocity and frustrates engineers. The same can be said for unused variables.

Unfortunately, this usability trick becomes a security problem when used with error handling. Take this code.

, err = fd.Write(p0[a:b]) if err != nil { return err } , err = fd.Write(p1[c:d])
if err != nil {
     return err
}
_, err = fd.Write(p2[e:f])
if err != nil {
     return err
}
// and so on

Most programmers eventually learn concepts like ‘DRY – Don’t Repeat Yourself’ and ‘YAGNI – You ain’t gonna need it” and then look at the if err != nil blocks aghast.

The quick solution? Use the blank identifier and throw away error messages.

message, _ := strconv.Atoi("Hello There")

When deadlines are looming and the compiler hates you, this shortcut becomes appealing. But it means you no longer have context when errors DO occur in your application, that you don’t have graceful escape processes (see defer and recover), and that you’re not using effective logging and messages to pinpoint where the problem is.

While the TryCatchFinally folk are okay with “uncontextualized exception e”, Go was built to make programmers intentionally handle errors close to the source. If the boilerplate makes your skin crawl, then take the time to read through > https://go.dev/blog/errors-are-values so you can handle errors in an effective and clean manner. But never discard errors with a blank identifier.

message, _ := strconv.Atoi("Hello There")
message, err := strconv.Atoi("Hello There")
if err != nil {
     log.Fatal(err)
}

Nobody uses Go Fuzzing

Go happens to support fuzzing in the standard toolchain since Go 1.18. Most golang codebases I have reviewed use the standard unit testing library, but haven’t adopted property-based fuzzing yet.

Fuzzing is a type of automated testing which continuously manipulates inputs to a program to find issues such as panics or bugs. It often finds edge cases and weird bugs in code that would be missed with normal software testing. Which is why it is great for finding security bugs. I’m not a fuzzing expert outside of following the standard tutorial and not letting my machine get cooked. But if you introduce property based fuzz testing to your codebase I guarantee you will find edge cases that you would never have thought about with standard unit or penetration testing.

Fuzzy Galah
The best way to learn how to fuzz is to read the https://go.dev/security/fuzz/ article to get a high level understanding about how it works, and then to follow this tutorial afterwards https://go.dev/doc/tutorial/fuzz.

Wrapping up

Thanks for reading through friends, I hope that you can take these security practices with you to help secure your next golang app. But don’t defer the basics like input validation either!

Next up we will have the next entry in our Galah Cyber blog series for Taming the Tools, how to be effective with various security tooling categories.