Golang HTTP Strict
Transport Security Guide: What
It Is and How to Enable It

stackhawk

StackHawk|October 3, 2021

What Golang HSTS is, what errors will show if it's missing, and how to enable HSTS.

Golang has so many advantages that lure developers into using it as a preferred back-end programming language. Among the lot is how it's perfectly suited for creating software in the cloud. The thing is, everything in the cloud requires HTTP for access, and this is where Golang HTTP Strict Transport Security (HSTS) comes into play.

This post explores the best ways to implement HSTS headers in Go projects. We'll visit a few concepts to make a case for the effort, touching on the benefits and reasons all your Golang projects should prioritize HTTPS over plain HTTP. Thereafter, we'll review a few best practices to restrict access to any Golang web applications to HTTPS.

Let's hit the ground with a few definitions.

What Is Golang HTTP Strict Transport Security?

In Golang projects, HTTP Strict Transport Security is a feature that directs traffic toward the HTTPS URL option when accessing web applications. This way, all attempts to navigate an application without transport layer certificates become impossible. This server behavior persists even when visitors have previously saved pages or bookmarked an HTTP-only version of the application.

Ordinarily, enforcing HTTPS traffic should be as easy as specifying a redirect strategy on the front end of any application. However, there's barely anything ordinary about the security implications and threats such an implementation would expose visitors to.

Threats Mitigated by HTTPS Redirects

While they're by no means the most elaborate strategy against attacks, HSTS headers provide a layer of security on the following fronts:

  1. Intranetwork attacks: When hosting applications on a local network (even in the cloud), an HSTS policy ensures all devices access the single truth version of your applications. In the event of an intruder trying to redirect traffic through their own access point, the certificates and encryption invoked with HTTPS should make it difficult to eavesdrop.

  2. False redirects: A solid HSTS implementation cancels all chances of hackers routing your traffic to a different domain. This usually happens when developers rely on HTTP 302 redirect strategies.

  3. Other vulnerabilities: Not having an HTTPS redirect set for your Golang application is a gateway for other attacks. Despite your good Golang development ethics, it will spell doom for all your efforts.

Threats HSTS Won't Save You From

Knowing what good an HSTS header does shouldn't leave you satisfied with the level of security implied. There are many areas that still need other headers and hackproofing through better coding practices. That said, the most elaborate HTTPS redirect strategy will not cover the following threat points:

  1. Phishing: Hackers can peddle a fake version of your website through clever domain naming schemes. They may even include HSTS redirects on that clone to boost visitor confidence. It's up to you to keep your traffic alert to parodies.

  2. Injection-type attacks: This applies if all you have behind the scenes is an HSTS specification. The resulting URL can still inject system commands and SQL to the server, causing a wide variety of damage.

  3. Unsafe programming habits: Having your application traffic encrypted is not a one-size-fits-all security fix. Regular code audits and tests are necessary to ensure overall security.

Now that you have a balanced view of the concept of HSTS in Golang, let's explore how it actually works with an implementation example.

Golang HSTS Implementation

Assuming you're creating Go projects with no frameworks embellishing your code, the best way to set HTTPS redirect headers is through a configuration file. You can then reference the now global variables in the header section of your main application file.

Before we break this process down, here's a look at the anatomy of an HSTS header in Go.

Golang
func Handler(h http.Handler) *Config {
	c := new(Config)
	*c = *DefaultConfig
	c.Next = h
	return c
}

func (c *Config) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if c.HTTPSRedirect && !c.isHTTPS(r) && !c.okloopback(r) {
		url := *r.URL
		url.Scheme = "https"
		url.Host = r.Host
		http.Redirect(w, r, url.String(), http.StatusMovedPermanently)
		return
	}

In this example, we have declared the boolean value of HTTPSRedirect in the configuration file pointed to by c. The second function implants the values in c onto w (the web accessible file) and joins it with the request made to the browser in the http.Redirect function.

Components of an HSTS Header

The redirect sequence in the code example above is not the prettiest, nor is it sufficient to cover all HSTS variables. As such, your configuration file should also include the following declarations:

  1. HSTS: A boolean that turns the entire redirect sequence on and off.

  2. HSTSMaxAge: A time frame during which traffic will redirect to HTTPS after the initial visit to the application. We usually denote this value in seconds, although you can explicitly make it an hour unit variable.

  3. HSTSIncludeSubdomains: If left undefined or set to false, then only your top domain redirects to HTTPS. This leaves out any subdomains you may create in the future. Ideally, you want this variable set to true for full coverage.

  4. HSTSPreload: The preload variable tells the browser to register your domain across a network of browsers such that even first-time visitors are redirected to HTTPS.

The resulting declaration in the config file would look like this.

Golang HTTP Strict Transport Security Guide: What It Is and How to Enable It - Picture 1 image

Golang HTTP Strict Transport Security configuration file.

Note: As alluded to in the section on threats covered (and not), other headers also appear in the same configuration file. All active Golang XSSProtection, CSP, and other headers not in the screenshot make for a better security outfit. But I digress.

Coming back to HSTS, the values above are applied in the header as follows:

Golang
//The Configuration file or section of the HSTS header implementation
var DefaultConfig = &Config{
	HTTPSRedirect:          true,
	HTTPSUseForwardedProto: ShouldUseForwardedProto(),

	PermitClearLoopback: false,

	ContentTypeOptions: true,

	CSP:          false,
	CSPBody:      "default-src 'self'",
	CSPReportURI: "",

	CSPReportOnly:          false,
	CSPReportOnlyBody:      "default-src 'self'",
	CSPReportOnlyReportURI: "",

	HSTS:                  true,
	HSTSMaxAge:            300 * 24 * time.Hour,
	HSTSIncludeSubdomains: true,
	HSTSPreload:           false,

	FrameOptions:       true,
	FrameOptionsPolicy: Deny,

	XSSProtection:      true,
	XSSProtectionBlock: true,
}

With the HSTS, HSTSMaxAge, HSTSIncludeSubdomains, and HSTSPreload values defined, the function that calls them up would look like this:

Golang
if c.HSTS && c.isHTTPS(r) {
		v := "max-age=" + strconv.FormatInt(int64(c.HSTSMaxAge/time.Second), 10)
		if c.HSTSIncludeSubdomains {
			v += "; includeSubDomains"
		}
		if c.HSTSPreload {
			v += "; preload"
		}
		w.Header().Set("Strict-Transport-Security", v)
	}

After this function, the Strict-Transport-Security header will have global effects on your Golang application.

If you're using either of the Go frameworks to build your application, implementation of the HSTS header would be as easy as adding packages that apply to your header requirements. This can be as simple as running a 'generate' command on your project when using the StalkR repository implementation.

Add Automated Security Testing to Your Pipeline

Conclusion & a Couple Golang HSTS Header Best Practices

HTTP Strict Transport Security headers are a must for your Golang web applications. While you don't get "out of this world" levels of security, when combined with other headers, HSTS makes for a strong first line of defense against attacks.

While you get on your way to creating new HSTS headers, keep in mind that the max-time duration you set renders them inactive once lapsed. This is the only upkeep required for the header, after which you may want to force user devices to clear the cache and load fresh instances of your web application.

When your application scales, it's best to have a mechanism that checks the efficacy of your HSTS headers before the public takes your app for a spin. For this, tools like StackHawk come in handy.

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.


StackHawk  |  October 3, 2021