Skip to main content

Weak credential policy - Password Expiration

Need

Implementation of a strong credential policy with password expiration

Context

  • Usage of Go 1.16 for developing high-performance and efficient applications
  • Usage of AWS SDK for Go to interact with AWS services
  • Usage of AWS SDK Go's session package for managing AWS sessions
  • Usage of AWS IAM service for managing access to AWS resources
  • Usage of gin-gonic/gin for building web applications in Go
  • Usage of net/http for creating HTTP servers in a Node.js application

Description

Non compliant code

package main

import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/gin-gonic/gin"
"net/http"
)

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

r.GET("/awsPolicy", func(c *gin.Context) {
sess, err := session.NewSession(&aws.Config{
Region: aws.String("us-west-2")},
)

// Create IAM service client
svc := iam.New(sess)

result, err := svc.GetAccountPasswordPolicy(&iam.GetAccountPasswordPolicyInput{})

if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

c.JSON(http.StatusOK, gin.H{"passwordPolicy": result.PasswordPolicy})
})

r.Run() // listen and serve on 0.0.0.0:8080
}

This code snippet is a simple Go application using the Gin framework. It includes an endpoint (/awsPolicy) which returns the AWS IAM password policy for the account.

The vulnerability lies in the GetAccountPasswordPolicy function. This function retrieves the account password policy, including whether password expiration is enabled or not. If the EnablePasswordExpiration flag in the returned PasswordPolicy object is false, this means that the password expiration is not enabled, which is a security risk.

Good security practices suggest that credentials should be renewed every 90 days. Without automatic password expiration, old and potentially compromised passwords can continue to be used, increasing the risk of unauthorized access.

To fix this vulnerability, the EnablePasswordExpiration flag should be set to true, and the MaxPasswordAge should be set to 90 (for 90 days) in the AWS IAM policy.

Steps

  • Enable password expiration in the AWS IAM policy
  • Set the password expiration period to 90 days
  • Update the code to retrieve and enforce the password expiration policy
  • Handle any errors that may occur during the retrieval or enforcement of the policy

Compliant code

package main

import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/gin-gonic/gin"
"net/http"
)

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

r.GET("/awsPolicy", func(c *gin.Context) {
sess, err := session.NewSession(&aws.Config{
Region: aws.String("us-west-2")},
)

// Create IAM service client
svc := iam.New(sess)

// Set the password policy
_, err = svc.UpdateAccountPasswordPolicy(&iam.UpdateAccountPasswordPolicyInput{
AllowUsersToChangePassword: aws.Bool(true),
HardExpiry: aws.Bool(true),
MaxPasswordAge: aws.Int64(90), // Set password expiration to 90 days
MinimumPasswordLength: aws.Int64(8),
PasswordReusePrevention: aws.Int64(5),
RequireSymbols: aws.Bool(true),
RequireNumbers: aws.Bool(true),
RequireUppercaseCharacters: aws.Bool(true),
RequireLowercaseCharacters: aws.Bool(true),
})

if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

result, err := svc.GetAccountPasswordPolicy(&iam.GetAccountPasswordPolicyInput{})

if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

c.JSON(http.StatusOK, gin.H{"passwordPolicy": result.PasswordPolicy})
})

r.Run() // listen and serve on 0.0.0.0:8080
}

The updated code now includes a call to UpdateAccountPasswordPolicy to set the password policy for the AWS account. The policy is set to:

  • Allow users to change their password
  • Enforce password expiration (hard expiry)
  • Set the maximum password age to 90 days
  • Set the minimum password length to 8 characters
  • Prevent password reuse for the last 5 passwords
  • Require at least one symbol, number, uppercase character, and lowercase character in the password

If there is an error while setting the password policy, the error is returned as a JSON response with a status code of 500 (Internal Server Error).

After setting the password policy, the code retrieves the current password policy using GetAccountPasswordPolicy and returns it as a JSON response. If there is an error while retrieving the password policy, the error is returned as a JSON response with a status code of 500 (Internal Server Error).

References