Insecure encryption algorithm - MD5 - Go

Insecure encryption algorithm - MD5 - Go

Need

Implementation of secure encryption algorithms for password hashing

Context

  • Usage of Go 1.16 for building high-performance and efficient applications
  • Usage of gin-gonic/gin for building web applications and APIs in Go

Description

Non compliant code

        package main

import (
	"crypto/md5"
	"encoding/hex"
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	r.POST("/login", func(c *gin.Context) {
		username := c.PostForm("username")
		password := c.PostForm("password")

		hashedPassword := GetMD5Hash(password)

		// Assume we have a function to validate the username and hashed password
		if ValidateUser(username, hashedPassword) {
			c.JSON(http.StatusOK, gin.H{
				"message": "Successfully logged in",
			})
		} else {
			c.JSON(http.StatusBadRequest, gin.H{
				"message": "Invalid username or password",
			})
		}
	})
	r.Run()
}

func GetMD5Hash(text string) string {
	hasher := md5.New()
	hasher.Write([]byte(text))
	return hex.EncodeToString(hasher.Sum(nil))
}

func ValidateUser(username string, hashedPassword string) bool {
	// Assume we have a function to validate the username and hashed password
	return true
}
        
        

The code above is a simple Go web application using the Gin framework. It provides a /login endpoint where users can submit their username and password.

The vulnerability lies within the GetMD5Hash function. This function uses the insecure MD5 algorithm to hash the user's password. MD5 is considered insecure because it's susceptible to hash collisions, meaning different inputs can produce the same hash output. This makes it easier for attackers to crack the hashed password using techniques such as rainbow tables or brute force attacks.

The hashed password is then passed to the ValidateUser function to check if the username and password combination is valid. If an attacker can crack the hashed password, they can potentially gain unauthorized access to the user's account.

Steps

  • Replace the use of MD5 algorithm with a stronger and more secure hashing algorithm, such as bcrypt or Argon2.
  • Update the GetMD5Hash function to use the new hashing algorithm.
  • Update the ValidateUser function to use the new hashing algorithm for password validation.
  • Ensure that the new hashing algorithm is properly implemented and configured with appropriate parameters, such as salt and iteration count, to enhance security.
  • Consider implementing additional security measures, such as password complexity requirements and account lockouts, to further protect against password attacks.
  • Regularly review and update the application's security practices to stay up-to-date with the latest best practices and recommendations.

Compliant code

        package main

import (
	"golang.org/x/crypto/bcrypt"
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	r.POST("/login", func(c *gin.Context) {
		username := c.PostForm("username")
		password := c.PostForm("password")

		hashedPassword, err := HashPassword(password)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"message": "Error hashing password",
			})
			return
		}

		// Assume we have a function to validate the username and hashed password
		if ValidateUser(username, hashedPassword) {
			c.JSON(http.StatusOK, gin.H{
				"message": "Successfully logged in",
			})
		} else {
			c.JSON(http.StatusBadRequest, gin.H{
				"message": "Invalid username or password",
			})
		}
	})
	r.Run()
}

func HashPassword(password string) (string, error) {
	bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
	return string(bytes), err
}

func ValidateUser(username string, hashedPassword string) bool {
	// Assume we have a function to validate the username and hashed password
	return true
}
        
        

The original code was using the MD5 hashing algorithm to hash passwords. MD5 is considered insecure for hashing passwords due to its speed and the availability of rainbow tables.

The updated code uses the bcrypt hashing algorithm, which is currently recommended for password hashing. Bcrypt is designed to be slow and it includes a salt to protect against rainbow table attacks.

The HashPassword function generates a bcrypt hash of the password. The cost parameter of bcrypt.GenerateFromPassword is set to 14, which determines the complexity of the hash. The higher the cost, the more computationally expensive the hash is to generate and verify, providing better resistance against brute force attacks.

The ValidateUser function should be updated to compare the hashed password with the stored hash using bcrypt.CompareHashAndPassword. This function is safe against timing attacks.

In addition to using a secure password hashing algorithm, it's recommended to implement other security measures such as password complexity requirements and account lockouts after a certain number of failed login attempts. Regularly reviewing and updating security practices is also important to stay up-to-date with the latest recommendations.

References