Insecure or unset HTTP headers - Strict Transport Security - Go

Insecure or unset HTTP headers - Strict Transport Security - Go

Need

Enforce strict transport security by setting appropriate HTTP headers

Context

  • Usage of Go 1.16 for developing high-performance and concurrent applications
  • Usage of Gin for building web applications in Go

Description

Non compliant code

        package main

import (
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	r.GET("/", func(c *gin.Context) {
		c.String(200, "Hello, World!")
	})
	r.Run() 
}
        
        

The above code is a simple Go application using the Gin framework. It sets up a HTTP server that responds with "Hello, World!" to GET requests at the root ("/") URL.

However, this code is vulnerable due to missing the Strict-Transport-Security HTTP header. This is a security flaw as it allows potential man-in-the-middle attacks. Without the Strict-Transport-Security header, the browser would not be forced to use secure connections, making it possible for attackers to intercept or manipulate the communication.

Also, if the Strict-Transport-Security header is present but the max-age is too short, it can also lead to vulnerabilities. The max-age directive specifies the amount of time, in seconds, that the browser should only access the server in a secure fashion. If this is too short, there could be periods where the browser could access the server in a non-secure fashion, leading to potential security risks.

Steps

  • Enable HTTPS for your application by obtaining an SSL/TLS certificate.
  • Configure your server to redirect all HTTP requests to HTTPS.
  • Set the Strict-Transport-Security (HSTS) header in your server's response.
  • Specify a reasonable max-age value for the HSTS header to ensure long-term protection.
  • Consider including the includeSubDomains directive in the HSTS header if applicable.
  • Test your application thoroughly to ensure that all HTTP requests are redirected to HTTPS.
  • Monitor your application's security headers regularly to ensure they are properly set.

Compliant code

        package main

import (
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()

	r.Use(func(c *gin.Context) {
		c.Header("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
		c.Next()
	})

	r.GET("/", func(c *gin.Context) {
		c.String(200, "Hello, World!")
	})

	r.RunTLS(":443", "/path/to/ssl/cert.pem", "/path/to/ssl/key.pem")
}
        
        

The updated code now includes the Strict-Transport-Security (HSTS) HTTP header in all server responses. This is achieved by adding a middleware function that sets the header before processing the request.

The Strict-Transport-Security header is set with a max-age of 31536000 seconds (1 year), and the includeSubDomains directive. This means that the browser should remember for one year that this site is only to be accessed using HTTPS, even if a link or redirect attempts to use HTTP. This includes all subdomains as well.

The RunTLS function is used instead of Run to start the server with HTTPS support. The paths to the SSL certificate and key files are provided as arguments. Please replace "/path/to/ssl/cert.pem" and "/path/to/ssl/key.pem" with the actual paths to your SSL certificate and key files.

Please note that you need to obtain a valid SSL/TLS certificate from a Certificate Authority (CA) and configure your server to use it. Self-signed certificates can be used for testing, but they will not be trusted by browsers in a production environment.

After these changes, all HTTP requests will be redirected to HTTPS, and the server will always include the HSTS header in its responses, which mitigates the risk of man-in-the-middle attacks.

References