Implementation of a strong credential policy with password expiration
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.
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).