logo

Uncontrolled external site redirect - Host Header Injection - Go


Need

Prevention of unauthorized external site redirects


Context

  1. Usage of Go 1.16 for building high-performance and scalable applications
  2. Usage of gin-gonic/gin for building web applications in Go

Description

Insecure Code Example

package main

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

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

	r.GET("/redirect", func(c *gin.Context) {
		host := c.Request.Host
		c.Redirect(302, host)
	})

	r.Run()
}

The above code is a simple `Go` application using the `Gin` framework. It listens for GET requests at the "/redirect" endpoint. When a request is received, it retrieves the `Host` from the `Request` Header and redirects the client to the URL specified in the `Host` header. The vulnerability lies in the fact that the application does not validate or sanitize the `Host` header before using it in the `Redirect` function. This means an attacker can manipulate the `Host` header to redirect the client to any destination, potentially leading to phishing attacks or other security issues. This is commonly referred to as an Uncontrolled External Site Redirect vulnerability or a Host Header Injection vulnerability.

Steps

  1. Validate and sanitize the input from the Host header.
  2. Implement a whitelist of allowed redirect destinations.
  3. Use a predefined redirect URL instead of relying on the Host header.
  4. Ensure that the redirect URL is properly encoded to prevent injection attacks.
  5. Consider using a secure redirect method (e.g., 301 or 307) instead of 302.

Secure Code Example

package main

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

var allowedHosts = []string{"www.example.com", "example.com"}

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

	r.GET("/redirect", func(c *gin.Context) {
		host := c.Request.Host
		if isValidHost(host) {
			c.Redirect(307, host)
		} else {
			c.String(400, "Invalid host")
		}
	})

	r.Run()
}

func isValidHost(host string) bool {
	parsedHost, err := url.Parse(host)
	if err != nil {
		return false
	}

	for _, allowedHost := range allowedHosts {
		if parsedHost.Host == allowedHost {
			return true
		}
	}

	return false
}

The updated code includes a function `isValidHost` that validates the host against a predefined list of allowed hosts. This function is used in the redirect handler to check the validity of the host before redirecting. If the host is not valid, the server responds with a 400 status code and a message "Invalid host". The `isValidHost` function uses the `url.Parse` function from the `net/url` package to parse the host. This function returns an error if the host is not a valid URL, which is used to immediately return false from the `isValidHost` function. The list of allowed hosts is defined as a slice of strings. This list can be updated to include all hosts that should be allowed to redirect. The redirect status code has been changed from 302 to 307. This is a more secure redirect method that does not allow the HTTP method to change during the redirect. This solution mitigates the risk of Host Header Injection by ensuring that only valid and allowed hosts can be used in the redirect. It also prevents potential injection attacks by properly encoding the redirect URL.


References

  • 023 - Uncontrolled external site redirect - Host Header Injection

  • Last updated

    2023/09/18