Use Golang? These Mistakes Could Compromise Your App’s Security

By | 2018-08-22T10:32:17+00:00 August 14th, 2018|

The Go Programming Language, sometimes referred to as Golang, is Google’s new programming language. It was released in 2009 and has developed a growing fanbase of developers.

The TIOBE index, which measures the popularity of programming languages at any given time, has shown steady growth in popularity for Go since it was released. In May 2015, Go was #122 on the TIOBE index. In July 2017, it had risen to #10. It currently sits at #18.

Go has found a nice niche among programming languages due to its lightweight architecture and suitability for use in microservice architecture. Large banks, The New York Times, Apple, Google, and Microsoft are all using Go in some capacity. It’s a first-class citizen in Microsoft Azure’s cloud services and AWS Lambda.

Developers using newer languages may not be aware of security mistakes which can lead to major vulnerabilities in their applications. Go doesn’t provide a lot of extra capabilities “out-of-the-box”, so the development community is still figuring out the best packages and frameworks to use for additional functionality.

Relatively immature helpers packages often appear and gain popularity quickly. Unfortunately, if these packages are unvetted by the security community, they can introduce vulnerabilities. Developers using these packages may unwittingly put their applications at risk.

In this post, we’ll outline common security issues with Go applications, including one found by one of Hunter2’s own developers.

XSS using text/template

Cross-site scripting (XSS) occurs when user input is used as output to the browser without being validated or encoded. Attackers can use this vulnerability to inject code into the browsers of visitors.

XSS attacks can be used to redirect users to phishing sites, steal cookies and session information, and can even rewrite portions of the web page the user is visiting.

The Go template package implements data-driven templates for creating textual output. Input from a file or from a user can be used to generate HTML output using the text/template package. For example, see the following code:

package main

import {
	“encoding/json”,
	“io/ioutil”,
	“net/http”,
	“os”,
	“text/template”
}

type TodoPageData struct {
	PageTitle string
	Todos []Todo

}

type Todo struct {
	Title string “json:title”
	Done bool “json:done”
}

func (t Todo) ToString() string {
	bytes, _ := json.Marshal(t)
	return string(bytes)
}

func getTodos() []Todo {
	todos := make([]Todo, 3)
	raw, _ := ioutil.ReadFile(“./todos.json”)
	json.Unmarshal(raw, &todos)
	return todos

}

func main() {
tmpl := template.Must(template.ParseFiles(“index.html”))

http.HandleFunc(“/”, func(w http.ResponseWriter, r *http.Request) {
	data := TodoPageData {
		PageTitle: “My Todos!”,
		Todos: getTodos(),
	}
	
	tmpl.Execute(w, data)

})

http.ListenAndServe(“:” + os.Getenv(“PORT”), nil)
}

This is a simple web server that builds a todo list using data in a JSON file. The file is read and then the data within is used to generate HTML for the client. If you change the JSON file to include

<script>alert(‘xss’)</script>

, the script would execute. This application is vulnerable to XSS due to a mistake that’s easy to make.

Look again at the import statement in the code. This attack is possible because the app uses text/template to render Go templates. Text/template doesn’t encode output which is sent to the browser as HTML.

It should instead use html/template, which generates properly escaped text output appropriate for rendering on an HTML page. This means that text such as will be encoded as HTML entities. The previous tags will be sent as “, rather than be interpreted by users’ browsers as code.
Developers frequently confuse text/template for html/template, but knowing about the difference ahead of time will help you to not make the same mistake.

You should consider html/template as the safe and appropriate choice whenever your final output source is HTML, rather than raw text. In this case, we’re rendering output in the browser, so we should use html/template when building the content to display in this page.

CSRF Protection using Gorilla

Cross site request forgery (CSRF) is an attack which tricks users into performing unwanted actions on a web application in which they are currently authenticated. If your application is vulnerable to CSRF, even two-factor authentication or other complex authentication schemes won’t prevent attacks.

A CSRF attack uses the session cookies provided to an authenticated user to post requests to the server that the victim didn’t intend. For example, an application used for money transfer services could be hijacked to transfer money to an attacker’s account by tricking the victim into clicking a button that sends the authenticated request.

A page’s HTML can be viewed by anyone, so it’s not difficult for an attacker to build a page such as this:

<html>
  <form action="http://target.example.com" method="POST">
    <input name="amount" type="hidden" value="9001">
    <input name="email" type="hidden" value="attacker@example.com">
    <button>Click to download 1 bitcoin</button>
  </form>
</html>

All the attacker has to do is match the input names of the original form. This malicious page can be hosted anywhere and a link sent to an unsuspecting victim via a phishing email. The button is pressed and $9,001 is sent to the attackers account.

In order to protect against requests from being forged, or from being recorded and replayed by an attacker, we need to add a random one-time token to each request. This unique, random string will identify that the request came from the original user. This token is called a CSRF token.

Rather than implementing our own from scratch, we can use the gorilla/csrf middleware library.

To get started, first install the package:

go get github.com/gorilla/csrf

Then add “github.com/gorilla/csrf” to the imports of your main.go file.

Finally, configure the CSRF protection and add it to a http.Handle function.

CSRF := csrf.Protect(
  []byte("add-32-byte-long-secret-key-here")
)

http.Handle("/", CSRF(r))

Please note that this is just one implementation. Check out the Gorilla docs for more examples of how to include this into your web application.

By default, the CSRF cookie will be set to HttpOnly with the Secure flag set to true. This will make sure that CSRF tokens will be free from tampering between the client and server and can’t be accessed by JavaScript.

In production the “add-32-byte-long-secret-key-here” value should be a random string read in from a secure file, rather than stored directly in version-controlled code.

Once the configuration is complete, you can add the CSRF token to the HTML page using the templates library. Add the following code to the function within your application that serves the form you want to protect:

tmpl.Execute(w, map[string]interface{}{
  csrf.TemplateTag: csrf.TemplateField(r),
})

Then add {{ .csrfField }} to the form of the page you want to protect.

Now, your app is protected against CSRF. Gorilla will add a random value to the form which attackers can’t guess. This prevents unwanted actions from being taken.

Security Misconfiguration with JWTs

If your head is spinning at the sight of yet another acronym, don’t despair. This is the last one, I promise.

JSON Web Tokens (JWTs) are compact JSON objects used to transmit information securely between endpoints. JWTs can be “signed” using a secret key, which provides assurance that the token hasn’t been changed and you trust the entity sending it.

The jwt-go package provides a Go implementation for JWTs. Make sure you use the latest version of jwt-go, or at least 3.0 and above. Previous versions allowed developers to use a “None” signature on JWTs. JWTs that aren’t signed can be easily spoofed by an attacker. The jwt-go package prior to version 3.0 was also vulnerable to Hashed Message Authentication Code (HMAC) spoofing and timing attacks.

JWTs are often used to verify claims from a party. For instance, your company server creates a JWT indicating you are an employee. The JWT is sent to a third party API which trusts your company’s server. Therefore, you are allowed access to the third-party API based on the claim that you are an employee of said company.

An easy mistake to make with JWTs is to leave the secret key used to sign them in a location attackers can access. For example, if your code has a hard-coded secret key and is hosted on GitHub, anyone with access to the repository can see your secret key. Or maybe your secret key is stored on a publicly accessible S3 bucket.

Either way, an attacker can easily take advantage of this mistake. Go to the jwt.io debugger and inspect the JWT example provided. There are three parts: the header, the payload, and the signature. In the signature section you see a box labeled “Your-256-bit-secret.”

The attacker simply changes the payload, perhaps to give them administrator access to the API. Then, they sign the token with your exposed secret key and send it along. Your app will not notice anything has gone wrong.

Here are the rules of thumb for JWTs:

  • Don’t send them over HTTP
  • Don’t place them in URL request parameters
  • Make sure your secret key remains a secret
  • Use the latest version of jwt-go

Don’t let security misconfiguration derail your token-based authentication.

Revel Hijacker

Revel is a popular web framework for the Go language. Recently, one of Hunter2’s developers found a security bug in the implementation of websockets within the Revel example chat application.

This particular sample application is vulnerable to cross-site websocket hijacking, which allows cross domain requests to occur over a websocket without validation.

In the sample chat application, a malicious attacker could create a websocket request from any domain and point it to the domain of the chat application. For example, using code like this:

ws = new WebSocket('ws://xxxx.com/websocket/room/socket?user=yyyy')

This code allows the attacker to impersonate a user (the value of the “user” parameter) and chat with others in the chat room.

You may be thinking, “What’s the big deal with that?” However, if authentication details are sent along with websocket requests, these could be sent to the attacker. An example of complete session compromise using cross-site websocket hijacking can be seen here.

So how do you fix this issue? Let’s use Revel’s chat app as an example. We’ll patch Revel itself to show how to verify the domain of a request before processing.

The http.go file in the Revel framework as-is doesn’t pay attention to the Origin HTTP header. We’ll change that. In your local copy of http.go, update the Request object to hold the origin:

Origin string

Then update the SetRequest function to store the value of the origin header in the request.

req.Origin = req.Header.Get(“Origin”)

Finally, in the actual chat application, update /app/controllers/websocket.go to enforce the same-origin policy for websockets. Add the following code to the beginning of the RoomSocket function:

// Make sure origin is validated
If c.Request.Origin != <your valid host> {
	return nil
}

Replace with the valid host for your domain, which you can store in an environment variable to make it easy.

This helps, although the Origin header can be spoofed. So a more complete solution would be similar to CSRF protection. You’ll want to create a session-specific random token. Each user will receive the token when a websocket connection is made. The user sends it back with each message and the server verifies it. We’ll leave that implementation as an exercise for the reader.

For more information about this specific vulnerability, check out the GitHub issue here.

The cross-site websocket hijacking vulnerability can be easily missed but can have major consequences. If a developer takes the sample code and uses it to create a production application, there could be serious consequences. Be careful.

Dependency Version Pinning

Most applications require several dependencies to handle key pieces of functionality. Dep and vgo can be used to manage these dependencies in Go. These libraries don’t introduce vulnerabilities themselves, but can lock you into vulnerable dependencies if you’re not careful.

We’ll focus on dep as an example. Dep allows you to provide a specific version of a dependency so you always use that version. This avoids breaking changes when a new version of that particular library is released.

Dep uses the Gopkg.toml file to define what dependencies are required and what versions you need (among other options). The “[[constraints]]” section of the file can be used to tell dep what specific version, branch, or revision of a library you need. A danger appears when a vulnerability is found in a certain library, such as the “zip slip” vulnerability in the archiver library, and you continue to use the vulnerable version even after a patch is released.

You can fix this by making sure all of your libraries are up-to-date. Running “dep ensure -update github.com/foo/bar” in a terminal will update a specific dependency to its latest version. You can also change the “[[constraints]]” section of the Gopkg.toml file to only use a certain version or a specific branch of a dependency. Configure dep to always use the master branch of the dependency repository., ensuring you get the latest patches as they’re released.

above a certain version number. For example, you can configure dep to use version 3.0 and above for a certain library that has vulnerabilities in version 2.x.

Dependency management is often tricky. Find a balance that keeps your application free from vulnerable dependencies while not introducing too much overhead.

Go Make Your Code Safer

See what I did there? Anyway, new languages can mean great benefits and features for developers. Go is a language made for cloud native applications and containers. In fact, Go is how containers like Docker are implemented.

However, new languages lead to new packages and frameworks which may not have been fully vetted as to security. Developers using these frameworks need to understand how to properly use them to keep their applications safe.

That’s why Hunter2 exists. It’s our mission and goal to make sure every developer is equipped to protect applications no matter what language they’re using. We’ll be your guide whether the language is old or new. In fact, our Go labs include practice exercises for each of the vulnerabilities we covered in this post and more. So check us out if you’re ready to start learning.

Go is a great language and is growing in popularity. Now that you’ve learned about some of the common pitfalls of the language and frameworks it uses, you’re prepared to built great Go apps which create value and remain secure.

Go gopher by Renee French

Sign Up for Updates