IAM user does not use long-lived credentials to access services other than IAM or STS
IAM users are assessed for activity using long-lived access keys. Use of static credentials to access services other than IAM or STS indicates reliance on permanent keys instead of temporary role-based credentials.
Risk
Persistent access keys enable attacker persistence and replay. Stolen keys allow off-network API calls for data exfiltration, privilege changes, and destructive actions, impacting confidentiality, integrity, and availability. Without expiry, the blast radius grows and containment is harder.
Run this check with Prowler
Run in Prowler Cloudprowler aws --checks iam_user_with_temporary_credentialsRecommendation
Adopt temporary credentials via IAM roles and federation for humans and workloads. Remove or restrict long-term keys; if unavoidable, apply least privilege, require MFA, rotate aggressively, and monitor usage. Prefer short session durations and session conditions to limit blast radius.
Remediation
CLI
aws iam put-user-policy --user-name <example_resource_name> --policy-name deny-non-iam-sts-with-long-term-creds --policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Deny","NotAction":["iam:*","sts:*"],"Resource":"*","Condition":{"Null":{"aws:TokenIssueTime":"true"}}}]}'Terraform
# Attach an inline policy to block long-term creds from non-IAM/STS services
resource "aws_iam_user_policy" "deny_non_iam_sts_longterm" {
name = "DenyNonIamStsWithLongTermCreds"
user = "<example_resource_name>" # Critical: target the affected IAM user
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Deny"
NotAction = ["iam:*", "sts:*"]
Resource = "*"
Condition = {
Null = { "aws:TokenIssueTime" = "true" } # Critical: denies when no session token (long-lived creds)
}
}]
})
}NativeIaC
# Attach a policy to block long-term creds from accessing non-IAM/STS services
Resources:
DenyLongTermNonIamSts:
Type: AWS::IAM::Policy
Properties:
PolicyName: DenyNonIamStsWithLongTermCreds
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Deny
NotAction:
- iam:*
- sts:*
Resource: "*"
Condition:
Null:
aws:TokenIssueTime: "true" # Critical: denies when no session token (i.e., long-lived creds)
Users:
- <example_resource_name> # Critical: attach to the affected IAM userOther
Check Code
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.iam.iam_client import iam_client
class iam_user_with_temporary_credentials(Check):
def execute(self) -> Check_Report_AWS:
findings = []
for (
user_data,
last_accessed_services,
) in iam_client.user_temporary_credentials_usage.items():
user_name = user_data[0]
user_arn = user_data[1]
report = Check_Report_AWS(
metadata=self.metadata(),
resource={"name": user_name, "arn": user_arn},
)
report.resource_id = user_name
report.resource_arn = user_arn
report.region = iam_client.region
# Search user in iam_client.users to get tags
for iam_user in iam_client.users:
if iam_user.arn == user_arn:
report.resource_tags = iam_user.tags
break
report.status = "PASS"
report.status_extended = f"User {user_name} doesn't have long lived credentials with access to other services than IAM or STS."
if last_accessed_services:
report.status = "FAIL"
report.status_extended = f"User {user_name} has long lived credentials with access to other services than IAM or STS."
findings.append(report)
return findings