home
navigate_next
Blog
navigate_next

Security At the Source - The Go Way

Security At the Source - The Go Way
Madhumita Yadav
Security At the Source - The Go Way

Tightening source code, in other terms performing Secure code review, is the process organizations go through to identify and fix potentially risky security vulnerabilities in the late stages of the development process. As the last threshold before an app is released, secure code reviews are an integral part of the security process. They serve as a sort of final review to check that your code is safe and sound and that all dependencies and controls of the application are secured and functional.


The OWASP Code Review Guide, penned by Jeff Williams, says it well: “The code is your only advantage over the hackers. Don’t give up this advantage and rely only on external penetration testing. Use the code.”

Basically over the last few decades industry is focusing to handle this in below two approaches -

SAST

( Static application security testing)

  • Static application security testing (SAST) is a great
    application security testing method that has the flexibility to
    perform in various SDLC processes.
  • SAST can be integrated directly into the development
    environment.
  • This enables developers to monitor their code constantly

DAST

( Dynamic application security testing )

  • A DAST test can look for a broad range of vulnerabilities,
    including input/output validation issues that could leave an
    application vulnerable to cross-site scripting or SQL
    injection.
  • A DAST test can also help spot configuration mistakes and
    errors and identify other specific problems with applications

Why it's important

Security code reviews focus on finding flaws in each of the following areas: Authentication, authorization, security configuration, session
management, logging, data validation, error handling, and encryption.

Several weaknesses (flaws) can affect each of the preceding security mechanisms. Flaws in the handling of passwords often affect
authentication. Flaws related to the type of information included in a message often affect error handling. Flaws in regular expressions often
affect data validation.

Secure code review is probably the single most effective technique for identifying security bugs early in the system development life
cycle. When used together with automated and manual penetration testing, code review can significantly increase the cost-effectiveness of an
application security verification effort

Manual secure code review provides insight into the “real risk” associated with insecure code… A human reviewer can understand the relevance
of a bug or vulnerability in code. Context requires a human understanding of what is being assessed

Nowadays since most of the applications are being written in Golang , lets deep dive into the various methodologies, tools we can utilize to
perform the Secure Code review in an efficient way.

https://github.com/securego/gosec
https://www.sonarqube.org/features/multi-languages/go/

Golang’s adoption has been increasing over the years. Successful projects like Docker, Kubernetes, and Terraform have bet heavily on this
programming language. More recently, Go has been the de facto standard for building command-line tools. And for security matters, Go happens
to be doing pretty well in their reports for vulnerabilities, with only one CVE registry since 2002.
However, not having vulnerabilities doesn’t mean that the programming language is super secure. We humans can create insecure apps if we
don’t follow certain practices. For example, by following the secure coding practices from OWASP, we can determine how to apply these
practices when using Go. And that’s exactly what I’ll do this time. In this post, I’ll show you six practices that you need to consider when
developing with Go

1. Validate input entries

Validating entries from the user is not only for functionality purposes but also helps avoid attackers who send us intrusive data that could damage
the system. Moreover, you help users to use the tool more confidently by preventing them from making silly and common mistakes. For instance,
you could prevent a user from trying to delete several records at the same time.
To validate user input, you can use native Go packages like strconv to handle string conversions to other data types. Go also has support for
regular expressions with regexp for complex validations. Even though Go’s preference is to use native libraries, there are third-party packages
like validator. With a validator, you can include validations for structs or individual fields more easily. For instance, the following code validates
that the User struct contains a valid email address:

package main
import (
"fmt"
"gopkg.in/go-playground/validator.v9"
)
type User struct {
Email string `json:"email" validate:"required,email"`
Name string `json:"name" validate:"required"`
}
func main() {
v := validator.New()
a := User{
Email: "a",
}
err := v.Struct(a)
for _, e := range err.(validator.ValidationErrors) {
fmt.Println(e)
}
}
view rawDec19_1.md hosted with by GitHub

2. Use HTML templates

One critical and common vulnerability is cross-site scripting or XSS. This exploit consists basically of the attacker being able to inject malicious
code into the app to modify the output. For example, someone could send a JavaScript code as part of the query string in a URL. When the
application returns the user’s value, the JavaScript code could be executed. Therefore, as a developer, you need to be aware of this and sanitize
the user’s input.


Go has the package html/template to encode what the app will return to the user. So, instead of the browser executing an input like <script>al
ert(‘You’ve Been Hacked!’);</script>, popping up an alert message; you could encode the input, and the app will treat the input as a
typical HTML code printed in the browser. An HTTP server that returns an HTML template will look like this:

package main
import (
"html/template"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
param1 := r.URL.Query().Get("param1")
tmpl := template.New("hello")
tmpl, _ = tmpl.Parse(`{{define "T"}}{{.}}{{end}}`)
tmpl.ExecuteTemplate(w, "T", param1)
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
view rawDec19_2.md hosted with by GitHub

But there are also third-party libraries you can use when developing web apps in Go. For example, there’s Gorilla web toolkit, which includes
libraries to help developers to do things like encoding authentication cookie values. There’s also nosurf, which is an HTTP package that helps
with the prevention of cross-site request forgery (CSRF).

3. Protect yourself from SQL injections

If you’ve been a developer for a while, you might be aware of SQL injections, which is still number one on OWASP’s Top 10 list. However, there
are some specific things that you need to consider when using Go. The first thing you need to do is make sure the user that connects to the
database has limited permissions. A good practice is to also sanitize the user’s input, as I described in a previous section, or to escape special
characters and use HTMLEscapeString function from the HTML template package.


But, the most critical piece of code you’d need to include is the use of parameterized queries. In Go, you don’t prepare a statement in a
connection; you prepare it on the DB. Here’s an example of how to use parameterized queries:

customerName := r.URL.Query().Get("name")
db.Exec("UPDATE creditcards SET name=? WHERE customerId=?",
customerName, 233, 90)
view rawDec19_4.md hosted with by GitHub

However, what if the database engine doesn’t support the use of prepared statements? Or what if it affects the performance of queries? Well, you
can use the db.Query() function, but make sure you sanitize the user’s input first, as seen in previous sections. There are also third-party
libraries like sqlmap to prevent SQL injections.


Despite our best efforts, sometimes vulnerabilities slip through or enter our apps via third parties. To ensure that you protect your web apps from
critical attacks like SQL injections, consider an application security management platform like Sqreen.

4. Encrypt sensitive information

Just because a string is hard to read, like a base-64 format, doesn’t mean that the hidden value is secret. You need a way to encrypt information
that attackers can’t decode easily. Typical information that you’d like to encrypt are database passwords, user passwords, or even social security
numbers.


OWASP has a few recommendations of which encryption algorithms to use, such as bcrypt, PDKDF2, Argon2, or scrypt. Fortunately, there’s
a Go package that includes robust implementations to encrypt information like crypto. For instance, the following code is a sample of how to use b
crypt:

package main
import (
"database/sql"
"context"
"fmt"
"golang.org/x/crypto/bcrypt"
)
func main() {
ctx := context.Background()
email := []byte("john.doe@somedomain.com")
password := []byte("47;u5:B(95m72;Xq")
hashedPassword, err := bcrypt.GenerateFromPassword(password,
bcrypt.DefaultCost)
if err != nil {
panic(err)
}
stmt, err := db.PrepareContext(ctx, "INSERT INTO accounts SET
hash=?, email=?")
if err != nil {
panic(err)
}
result, err := stmt.ExecContext(ctx, hashedPassword, email)
if err != nil {
panic(err)
}
}
view rawDec19_5.md hosted with by GitHub

Notice that you still need to be careful about how you transmit the information between services. You wouldn’t like to send the user’s data in plain
text. It doesn’t matter if the app encrypts users’ inputs before persisting the data. Assume that someone on the internet could be sniffing your
traffic and keeping request logs of your system. An attacker might use this information to correlate it with other data from other systems.

5. Enforce HTTPS communication

Nowadays, most browsers require that HTTPS works on every site. Chrome, for example, will show you an alert if in the address bar if the site isn’
t using HTTPS. An Infosec team could have as a policy to enforce in-transit encryption for communication between services. So, to secure an intransit connection in the system isn’t only about the app listening in port 443. You also need to use proper certificates and enforce HTTPS to
avoid attackers downgrading the protocol to HTTP.

Here’s a code snippet for a web app that uses and enforces the HTTPS protocol:

package main
import (
"crypto/tls"
"log"
"net/http"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
w.Header().Add("Strict-Transport-Security", "max-age=63072000;
includeSubDomains")
w.Write([]byte("This is an example server.\n"))
})
cfg := &tls.Config{
MinVersion: tls.VersionTLS12,
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.
CurveP384, tls.CurveP256},
PreferServerCipherSuites: true,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
},
}
srv := &http.Server{
Addr: ":443",
Handler: mux,
TLSConfig: cfg,
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn,
http.Handler), 0),
}
log.Fatal(srv.ListenAndServeTLS("tls.crt", "tls.key"))
}
view rawDec19_6.md hosted with by GitHub

Notice that the app will be listening in port 443. The following line is the one enforcing the HTTPS configuration:

w.Header().Add("Strict-Transport-Security", "max-age=63072000;
includeSubDomains")
view rawDec19_7.md hosted with by GitHub

You might also want to specify the server name in the TLS configuration, like this:

config := &tls.Config{ServerName: "yourSiteOrServiceName"}
view rawDec19_7.md hosted with by GitHub

It’s always a good practice to implement in-transit encryption even if your web app is only for internal communication. Imagine if, for some
reason, an attacker could sniff your internal traffic. Whenever you can, it’s always best to raise the difficulty bar for possible future attackers.

6. Be mindful with errors and logs

Last, but definitely not least, are error handling and logging in to your Go apps.
To successfully troubleshoot in production, you need to instrument your apps properly. But you need to be mindful of the errors you show to
users. You wouldn’t like users to know what exactly went wrong. Attackers might use this information to infer which services and technologies
you’re using. Moreover, you have to remember that even though logs are great, they’re stored somewhere. And if logs end up in the wrong
hands, they can be used to build an upcoming attack into the system.
So, the first thing you need to know or remember is that Go doesn’t have exceptions. This means that you’d need to handle errors differently than
with other languages. The standard looks like this:

if err != nil {
// handle the error
}
view rawDec19_8.md hosted with by GitHub

Also, Go offers a native library to work with logs. The most simple code is like this:

package main
import (
"log"
)
func main() {
log.Print("Logging in Go!")
}
view rawDec19_9.md hosted with by GitHub

But there are third-party libraries for logging as well. A few of them are logrus, glog, and loggo. Here’s a small code snippet using logrus:

package main
import (
"os"
log "github.com/sirupsen/logrus"
)
func main() {
file, err := os.OpenFile("info.log", os.O_CREATE|os.O_APPEND,
0644)
if err != nil {
log.Fatal(err)
}
defer file.Close()
log.SetOutput(file)
log.SetFormatter(&log.JSONFormatter{})
log.SetLevel(log.WarnLevel)
log.WithFields(log.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
}
view rawDec19_3.md hosted with by GitHub

Finally, make sure you apply all the previous rules like encryption and sanitization of the data you put into the logs.

Major Go Frameworks:

  • Revel
    Revel is a high productivity web framework for Go that includes an array of extensive high-performance features, making it convenient for Go
    developers as they don’t need to seek external libraries to integrate to the framework. A standout feature is Revel’s Hot Code Reload tool which
    allows you to rebuild your project with every file change.
  • Beego
    Beego is an “open source framework to build and develop your applications in the Go way” in form of a fully developed framework, complete with
    its own web frameworks, logging library, and ORM. Beego includes Bee Tool, a feature which looks out for code changes and can run tasks once
    changes are detected. Beego is a great framework for busy developers, as it will save you many hours when it comes to launching a new project.
  • Protocol Buffers
    This may not be a Go-exclusive traditional framework, but definitely worth a mention. While Protocol Buffers is Google’s language and platform
    neutral mechanism for serializing structured data, Go developers are known to use this mechanism to define how their data should be structured.
    Protocol buffers is known for its ease-of-use and simplicity.
  • Gin Gonic
    If you’re familiar with Martini-like APIs, Gin Gonic is for you. Gin Gonic is a HTTP web framework written in Go (Golang), known to be a highperformance minimal framework including fundamental features and libraries

Go Security Vulnerabilities

With Go’s surge in popularity, it is crucial that Go applications are designed and built with security in mind. Click here to get your copy of the Go
Secure Coding Guide for a deeper dive into the world of secure development best practices in Go, along with many tips, tricks, and code
examples to help ensure the security of your Go. That said, there are a couple of stand out security vulnerabilities commonly affecting apps
written in Go:

Gometalinter

Go Metalinter is the TLDR version of this article — if you like the kitchensink approach of giving me everything, go metalinter comes with enough
things to check for all the tools. VS Code installs a good set of tools when it detects that you’re editing a Go project. Here’s how you can run all
the tools that come with metalinter

go get alecthomas/gometalinter
#Install the packages
gometalinter --install
gometalinter ./...

If you don’t like the shebang approach, I’ll go over some of the tools in go metalinter and others.

Gas

Go AST Scanner is a great project for catching security issues like whether you’re using MD5 somewhere or rand rather than crypto/rand
package. That kind of thing.
You can get it running relatively quickly too

go get github.com/GoASTScanner/gas/cmd/gas/...
gas ./...

Additionally, you can filter by different error types too. If you need to use something that will get flagged by gas, you can use #nosec to avoid the
alert. As usual, it’s always better to add a note on why you’re adding a #nosec for the next person.

safesql

Safesql checks against sql injection attacks — particularly helpful in ensuring that some user-supplied data isn’t going to cause SQL Injection
attacks.

goreportcard

Goreportcard is totally a sweet vanity project for me whenever I have an open-source project to check. You can get a web page with the
percentage score for your project based on a few tools.


I have now checked the report for my last significant open source project accord, and it’s dropped to 96%. This may not make your project more
secure in a direct sense, but getting it to 100% will feel good.

go vet/test

One of the tools I love is the go vet tool built into Go, which now breaks your tests as of Go 1.10 which has caught a lot of problems at least at
Mist.

dingo-hunter

I have never been able to run dingo hunter on a non-academic project with significant complexity but this is a static analyzer to model
concurrency and find deadlocks in Go code, the core code for models is written in Haskell and there’s some pi-calculus math behind it that I don’t
want to pretend to understand.

arrow_back
Back to blog