~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
🔒 Related Stories: Golang
💻 Free Content on Jobs in Cybersecurity | ✉️ Sign up for the Email List
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In my first post, I explained why you might want to use Go and some of the security benefits of Golang concurrency. Next, I explained how to install Go on an AWS EC2 Instance, including some cloud security recommendations and considerations.
If you're new to programming, you'll probably want to learn about if/then statements, for loops, and other general programming constructs first. These apply to almost every language. Many good programming books for beginners exist. Once you know what a switch statement does, for example, the reference below explains how to write one in Go.
As an experienced programmer, whenever I start learning a new language, I first want to understand the basic constructs and how that relates to the programming languages I already know. Each language has similarities and differences. This post does not explain how to write Go programs. That will come later. The information below is a high-level overview and reference of Golang programming syntax.
If you want to take a tour of go and try out some code in action, you can try out this nifty tour to see the constructs and type code online.

Go has some great documentation here on basic language syntax:
These are my cliff notes on that document plus a few others in the links below. I may add to this reference over time as I obtain clarity on a few points. My goal is to explore using Golang for some new penetration testing tools I want to build. I hope to follow go best practices, so I'll do a bit of research before I start copying and pasting code. I'll sprinkle in some secure programming tips along the way.
NOTE: When you see [...] in the code samples below, that indicates more code would be in place of the brackets and dots.formatting
- Don't worry about formatting or aligning documentation. Go handles all that with the gofmt (or fmt) and godoc command. No more arguing over formatting — go does it for you!
- Don't worry about line length.
- If you think a line is too long, return and tab.
- Golang is free from format string attacks due to the way it handles string variables (also explained more below).
- More on go formatting.
comments
//You can write a comment on a single line like this
/*
Here's a multi-line
comment
*/creating applications
- The go command is used to manage dependencies and build programs.
- For example, go build.
- Stay tuned for more in an upcoming blog post.
packages
- A collection of source files in a single directory that are compiled together.
- Functions, types, variables, and constants in the same package are visible to all other files in the same package.
- A package name starting with a capital letter will be public. A lower case letter indicates a private package.
- Packages are imported like this:
import "bytes"- Short names are best and preferred by Go standards.
- Don't use camel case when possible, or underscore.
- Go package names are determined by their directory structure so don't worry about naming conflicts.
- If you must use multiword names, the convention in Go is to use
MixedCapsormixedCapsrather than underscores.
modules
- Modules are how go manages dependencies.
- A module is a collection of packages that are released, versioned, and distributed together.
- A module has a go.mod file at the root of the directory.
- Modules may be downloaded from Github repos during build process (potential security problem, be careful with third-party code injection).
- Alternatively download from module the go proxy servers (the part that was was missing last time I used Go).
- Modules reference: https://golang.org/ref/mod
semicolons
- No semicolons in the source, though they are used behind the scenes.
- Position curly braces like this:
if i < f() {
g()
}- This may break:
if i < f()
{
g()
}if
- Curly braces are mandatory.
if x > 0 {
return y
}- Assign a value to a variable, then check it with if:
if err := file.Chmod(0400); err != nil {
log.Print(err)
return err
}for
- No while or do while. Use these:
//for init; condition; iterate
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
//looping over a structure of some kind
for key, value := range map1 {
map2[key] = value
}
//if you only need the key drop the value
for key := range m {
delete(m, key)
}
//use an underscore (blank identifier) if you don't need the key.
sum := 0
for _, value := range array {
sum += value
}switch
- Example from https://play.golang.org/p/8b5CajPcHn
i := 2
fmt.Print("write ", i, " as ")
switch i {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
case 3:
fmt.Println("three")
}- You can combine cases like this:
func shouldEscape(c byte) bool {
switch c {
case ' ', '?', '&', '=', '#', '+', '%':
return true
}
return false
}- Use break to terminate a switch. Documentation says this is not common.
- Determine a type:
var t interface{}
t = functionOfSomeType()
switch t := t.(type)
[...]functions
- Functions can return multiple values.
func nextInt(b []byte, i int) (int, int) {
[...]
return x, i
}- Parameters are given names and initialized with 0.
- A return statement with no names will return all the return values.
defer
- Defer lets you close a file immediately after declaration. It will be closed just before the function exits.
func Contents(filename string) (string, error) {
f, err := os.Open(filename)
if err != nil {
return "", err
}
defer f.Close()
[...]
//f.Close file after everything else is done via defer above.
}- A single defer statement can execute multiple actions.
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}- Defer is useful when tracing function calls.
- Deferred functions are executed in LIFO order.
new
- Allocates memory and zeroes it out. Does not initialize it.
- Due to the way Go handles memory you should be safe from security problems like a buffer overflow and use-after-free bugs.
make
- Creates slices, maps and channels only. Initializes memory.
- Example: Allocate an array of 100 ints and then creates a slice structure with length 10 and a capacity of 100 pointing at the first 10 elements of the array.
make([]int, 10, 100)arrays
- Arrays are values. Assigning one to another copies all the values.
- Passing an array to a function passes a copy, not a pointer.
- The size of an array is part of its type. [?]
- You can use pointers but don't. Use slices.
- Only used for a fixed-size data set.
- Example: https://tour.golang.org/moretypes/6
- Array of arrays is possible.
type Transform [3][3]float64 // A 3x3 array, or an array of arrays.slices
- Used more than arrays.
- Holds a reference to an underlying array.
- Typed by elements they contain, rather than size.
- One slice assigned to another refers to the same array.
- Changes made to an array in a function call will be visible to the caller.
- Slice length can change as long as it fits in the underlying array.
- Example: https://tour.golang.org/moretypes/7
- Slice of slices is possible.
type LinesOfText [][]byte // A slice of byte slices.maps
- Contains key value pairs. Example:
var timeZone = map[string]int{
"UTC": 0*60*60,
"EST": -5*60*60,
"CST": -6*60*60,
"MST": -7*60*60,
"PST": -8*60*60,
}- Fetch a value (does not need to be an integer):
offset := timeZone["EST"]- Map of boolean values
attended := map[string]bool{
"Ann": true,
"Joe": true,
[...]
}
if attended[person] { // will be false if person is not in the map
fmt.Println(person, "was at the meeting")
}- Determine if a value in a map exists.
_, present := aMap[v]- Set a variable (myVar) to the map value (aMap[v]) if it exists.
myVar, present := aMap[v]- Delete a map entry
delete(timeZone, "PDT")- No format string required. Examples:
fmt.Printf("Hello %d\n", 23)
fmt.Fprint(os.Stdout, "Hello ", 23, "\n")
fmt.Println("Hello", 23)
fmt.Println(fmt.Sprint("Hello ", 23))- For more see the fmt package: https://golang.org/pkg/fmt/
append
- Append the elements to the end of the slice and return the result.
x := []int{1,2,3}
x = append(x, 4, 5, 6)
fmt.Println(x)constants
const Pi = 3.14- Enumerated constants are created using the
iotaenumerator.
type ByteSize float64
const (
_ = iota // ignore first value by assigning to blank identifier
KB ByteSize = 1 << (10 * iota)
MB
GB
TB
PB
EB
ZB
YB
)variables
- Use keyword var.
- Package or function level.
- Example:
var c, python, java bool- or:
var (
home = os.Getenv("HOME")
user = os.Getenv("USER")
gopath = os.Getenv("GOPATH")
)init
- Set up state.
- Called after variable declarations and imported package initialization.
pointers
- Yes.
- More: https://tour.golang.org/moretypes/1
- More: https://golang.org/doc/effective_go.html#pointers_vs_values
interfaces
- Yes
- More: https://tour.golang.org/methods/9
- More: https://golang.org/doc/effective_go.html#interfaces_and_types
blank identifier (underscore)
- Represents a write-only value to be used as a place-holder where a variable is needed but the actual value is irrelevant.
if _, err := os.Stat(path); os.IsNotExist(err) {
fmt.Printf("%s does not exist\n", path)
}- In other words, you are required to pass something into a function but the value doesn't matter. Kind of how I used null in Java sometimes for optional parameters.
imports
- It is an error for unused imports to exist.
- Silence errors using the blank identifier while troubleshooting:
package main
import (
"fmt"
"io"
"log"
"os"
)
var _ = fmt.Printf // For debugging; delete when done.
var _ io.Reader // For debugging; delete when done.
[...]- Import a package only for its side effects without really using.
import _ "net/http/pprof"- Can also be used with interface checks.
embedding
- No subclassing.
- Embed a type within a structure or interface.
concurrency
- Values are passed around on channels, never shared by threads.
- Race conditions cannot occur, by design.
- Do not communicate by sharing memory; share memory by communicating.
goroutines
- A function executing concurrently with other goroutines in the same address space.
- If one is blocked, others continue to run.
- To run a call in a go routine, prefix with the go keyword.
go list.Sort() // run list.Sort concurrently; don't wait for it.- Sample function to call a goroutine:
func Announce(message string, delay time.Duration) {
go func() {
time.Sleep(delay)
fmt.Println(message)
}() // Note the parentheses - must call the function.
}- Function literals are closures: variables referred to by the function survive as long as they are active.
- Need channels to signal completion. See next section.
- More: https://tour.golang.org/concurrency/1
channels
- Allocated by make.
- Reference to underlying data structure.
ci := make(chan int) // unbuffered channel of integers
cj := make(chan int, 0) // unbuffered channel of integers
cs := make(chan *os.File, 100) // buffered channel of pointers to FilesA channel that allows the launching goroutine to wait for the sort to complete.
c := make(chan int) // Allocate a channel.
// Start the sort in a goroutine; when it completes, signal on the channel.
go func() {
list.Sort()
c <- 1 // Send a signal; value does not matter.
}()
doSomethingForAWhile()
<-c // Wait for sort to finish; discard sent value.- Receivers always block until there is data to receive.
- If the channel is unbuffered, the sender blocks until the receiver has received the value.
- If the channel has a buffer, the sender blocks only until the value has been copied to the buffer.
- If the buffer is full, sender waits until some receiver has retrieved a value.
- A buffered channel can be used like a semaphore, e.g. to limit throughput.
- Can create a channel of channels.
parallelization
- Launch processes across CPU cores.
- More: https://golang.org/doc/effective_go.html#parallel
errors
- Create an error type:
type error interface {
Error() string
}- Return an error:
func (e *PathError) Error() string {
return e.Op + " " + e.Path + ": " + e.Err.Error()
}panic
- Runtime error that stops the program.
var user = os.Getenv("USER")
func init() {
if user == "" {
panic("no value for $USER")
}
}recover
- Regain control after a panic so the program will not exit.
- Only useful in deferred functions.
func server(workChan <-chan *Work) {
for work := range workChan {
go doWork(work)
}
}
func doWork(work *Work) {
defer func() {
if err := recover(); err != nil {
log.Println("failed:", err)
}
}()
do(work) //panic will not halt the program due to the recover
}Future posts in this series on Getting Started with Go. Next up will most likely be something related to writing an actual program and some information about the go proxy for including third-party code in your project — the missing component from my earlier exploration of Golang. I'm curious to try it out!
Follow for updates.
Teri Radichel | © 2nd Sight Lab 2020
The best way to support this blog is to sign up for the email list and clap for stories you like. That also helps me determine what stories people like and what to write about more often. Other ways to follow and support are listed below. Thank you!
About Teri Radichel:
~~~~~~~~~~~~~~~~~~~~
Author: Cybersecurity for Executives in the Age of Cloud
Presentations: Presentations by Teri Radichel
Recognition: SANS Difference Makers Award, AWS Security Hero, IANS Faculty
Certifications: SANS
Education: BA Business, Master of Software Engineering, Master of Infosec
Company: Cloud Penetration Tests, Assessments, Training ~ 2nd Sight Lab
Like this story? Use the options below to support this blog.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
❤️ Clap
❤️ Referrals
❤️ Medium: Teri Radichel
❤️ Email List: Teri Radichel
❤️ Twitter: @teriradichel
❤️ Mastodon: @teriradichel@infosec.exchange
❤️ Facebook: 2nd Sight Lab
❤️ YouTube: @2ndsightlab
❤️ Buy a Book: Teri Radichel on Amazon
❤️ Request a penetration test, assessment, or training
via LinkedIn: Teri Radichel
❤️ Schedule a consulting call with me through IANS ResearchCybersecurity for Executives in the Age of Cloud
