Go is a language created inside Google's ranks for memory safety and concurrency. It made sense for development instances running (or developed) across distributed nodes as microservices were taking form. The term Golang refers to Go with "lang" specifying that we're referencing the programming language. The official website for the language made "Golang" a popular alias. All this, however, doesn't sanitize Go from Golang CSRF (cross-site request forgery).
This post will take you through some Golang CSRF vulnerabilities and guide you toward solutions with real-life code examples. A holistic approach to this guide requires that we set clear definitions of the problem scenario. We'll thus explore just how bad things can get when a CSRF attack makes it past your Go application's front end to the server-side. Only then will any solution make perfect sense. Strap in and enjoy the ride!
CSRF: A Recap
Cross-site request forgery is an attack strategy that takes advantage of every web application's operational design. For instance, the current state of an app's front end can be processed to change the state of resources and variables on the server-side. A CSRF attack typically starts with exploiting HTML verbs found throughout a typical page.
If you're working with a framework like gopher, for example, several routing default functions (methods) are provided out the box.
If your Golang application exposes these methods and forms to the user before sending a request, you're vulnerable to CSRF attacks. By studying these methods, an attacker can study the destination of your requests and forge some code to perform the usual actions, but with a few changes. Such changes could be changing the destination of a bank transfer to their details or simply deleting your profile.
The attack described is easy to confuse with XSS. However, CSRF must include some form of forgery (passing altered variables) to the server for processing and eventual altering of states to fit the intention of the attacker. You can refer to this passage for a better distinction of the two.
Golang Vulnerabilities Exposed by CSRF
Now, you may be wondering just how such an attack strategy applies for Go projects when the most prevailing use case for the language is with enterprise applications. Well, there's a horde of web frameworks built to use Go on the back end. This way, even as it is a compiled language, the strengths it brings into the stack (concurrency, etc.) can be appreciated all the same.
Common Golang Web Frameworks
There's been an explosion of Golang web framework options lately. Typically, these frameworks are all lightweight and operating system agnostic thanks to Go being cross-platform by nature. Let's review a few of these frameworks and toolkits for future reference.
1. Gorilla Go Toolkit
Gorilla is an extendable list of components and methods that you can freely adapt for your Go web applications. Named to raise awareness of the primate's plight, Gorilla's installation is as easy as cloning the required package into your existing Go project.
$ git clone git://github.com/gorilla/[package name here]
You may have noticed by this definition and method of use that it's actually not a framework.
2. Gin for Golang
Gin is a web framework created for Go web projects that require lightning-fast performance. The APIs applied in Gin are in some way similar to the ones in Golang's Martini web framework, with a little extra juice for performance enhancement (wink).
3. Go-admin
Go-admin is a framework that helps create administration back ends to visualize datastreams. You can add the project to an existing web application as the processor of any dynamic source of data as with common dashboard tools.
Now that you have a good idea of what you can do with Go on the front end, let's explore just how secure these implementations are.
Golang CSRF Examples
Let's use the HTML file from a hotel booking web app developed on the Revel Go framework to demonstrate the use of CSRF. Of particular interest is the booking confirmation form with code snippets shown below:
{%include "header.html" %}
<h1>Confirm hotel booking</h1>
<form method="POST" action="{%url "Hotels.ConfirmBooking" hotel.HotelId%}">
<p>
<strong>Name:</strong> {{hotel.Name}}
</p>
<p>
<strong>Address:</strong> {{hotel.Address}}
...
Then the sensitive part is visible as below:
...
<p>
<strong>Credit card #:</strong> {{booking.CardNumber}}
<input type="hidden" name="booking.CardNumber" value="{{booking.CardNumber}}">
<input type="hidden" name="booking.NameOnCard" value="{{booking.NameOnCard}}">
<input type="hidden" name="booking.CardExpMonth" value="{{booking.CardExpMonth}}">
<input type="hidden" name="booking.CardExpYear" value="{{booking.CardExpYear}}">
<input type="hidden" name="booking.Smoking" value="{{booking.Smoking}}">
</p>
<p class="buttons">
<input type="submit" value="Confirm" name="confirm">
<input type="submit" value="Revise" name="revise">
<a href="{%url "Hotels.Show" hotel.HotelId%}">Cancel</a>
</p>
</form>
Conducting a short reconnaissance session on this application, we can see how the payment data is in a rather raw form. Never mind the pointer variables, as at some point the user gets to input all that data manually. Couple this discovery with the fact that a known method (POST) sends the information across to a known destination (%url) and this makes the process risky.
A possible attack trajectory would go down this way:
The attacker sends a fake email from a domain with a subdomain containing the hotel's own, asking for confirmation or feedback or sorts. Anything to get the user past the login form.
Another email follows up with a discount offer on the guest's next stay.
Once the target opens the second email, a forgery of the entire form is generated with their usual payment details to maybe make a new booking for a different guest. Even worse, the form can be sent to a different destination where the CardNumber, NameOnCard, CardExpMonth, CardExpYear, and previously smoked (encrypted) CVV can be parsed for storage.
Using the same form from above, the final form data sent for processing would be processed to show the sensitive info in CSV format. Even though the form had tried to hide them with the hidden type attribute.
"some card number value (int)", "corresponding name", "card Exp month", "card Exp year", "cvv"
Need we say just how much risk befalls any guest who may have innocently shared their details? Instead, let's turn our focus on Golang CSRF solutions.
Golang CSRF Patches
Lucky for Go language enthusiasts, there exist several middleware options to patch Go framework apps and protect them from CSRF attacks. Each framework usually comes with a specific implementation of said middleware.
Take Gin, for example, which sanitizes the sessions created by users by obfuscating the authentication cookie with a secret variable. This implementation is extended onto Go projects using the gin-csrf package.
$ go get github.com/utrack/gin-csrf
On the code level, this would be the patch:
...
func main() {
r := gin.Default()
store := cookie.NewStore([]byte("secret"))
r.Use(sessions.Sessions("mysession", store))
r.Use(csrf.Middleware(csrf.Options{
Secret: "secret123",
ErrorFunc: func(c *gin.Context) {
c.String(400, "CSRF token mismatch")
c.Abort()
},
}))
...
The Gorilla toolkit also applies a middleware library to protect users from Golang CSRF attacks. The beauty of this particular implementation is its framework compatibility and coverage. This means you can apply fixes from the Gorilla library into Gin and other framework options.
Hackproofing Your Golang Apps
Kudos for reading this far! It should now be clear what CSRF means. So, too, how Golang web applications can fall prey to this type of attack. Say you elect to use "vanilla" Go to create your apps—removing the framework stance from the equation. The methods we discussed in the patches section should give you an idea of how to secure your apps.
True, so much more goes into fully hackproofing your Go apps. Our take on CSRF must (at the very least) leave you aware of the potential risks. It should be easy for you to start Golang projects that take security into consideration from their onset.
This post was written by Taurai Mutimutema. Taurai is a systems analyst with a knack for writing, which was probably sparked by the need to document technical processes during code and implementation sessions. He enjoys learning new technology and talks about tech even more than he writes.