Amazon Bedrock long-term API key has expired
AWS recommends Amazon Bedrock long-term API keys only for exploration; production workloads should use short-term API keys (session-scoped, valid up to 12 hours). This check fails for any active long-term Bedrock API key, escalating to critical severity when configured to never expire. Already-expired keys pass — they can no longer authenticate.
Risk
Long-term Bedrock API keys persist beyond a session until their stored expiration, and keys set to never expire grant indefinite access until manually revoked, enabling unauthorized inference, uncontrolled usage and spend, and activity that continues past timely revocation.
Run this check with Prowler
Run in Prowler Cloudprowler aws --checks bedrock_api_key_no_long_term_credentialsRecommendation
Use short-term Amazon Bedrock API keys for any non-exploratory workload — they are bound to the IAM principal's session, valid for at most 12 hours, scoped to a single Region, and can be auto-refreshed by the SDK. For existing long-term keys, delete the underlying IAM service-specific credential. If a long-term key must be retained for an exploration scenario, set an explicit short expiration and never select never expire.
Remediation
CLI
aws iam delete-service-specific-credential --user-name <username> --service-specific-credential-id <credential-id>Other
Check Code
from datetime import datetime, timezone
from prowler.lib.check.models import Check, Check_Report_AWS, Severity
from prowler.providers.aws.services.iam.iam_client import iam_client
# Days threshold above which a Bedrock long-term API key is considered effectively non-expiring.
NEVER_EXPIRES_THRESHOLD_DAYS = 10000
class bedrock_api_key_no_long_term_credentials(Check):
"""Amazon Bedrock long-term API keys should not be used outside of exploration.
AWS recommends short-term Bedrock API keys (session-scoped, valid up to 12 hours)
for any non-exploratory workload. ``ListServiceSpecificCredentials`` only enumerates
long-term keys, so every key inspected here is by definition a long-term credential.
PASS when the long-term key has already expired (it can no longer authenticate).
FAIL (critical) when the key is configured to never expire.
FAIL (high) for any other active long-term key.
"""
def execute(self):
findings = []
for api_key in iam_client.service_specific_credentials:
if api_key.service_name != "bedrock.amazonaws.com":
continue
if not api_key.expiration_date:
continue
report = Check_Report_AWS(metadata=self.metadata(), resource=api_key)
now = datetime.now(timezone.utc)
if api_key.expiration_date <= now:
report.status = "PASS"
report.status_extended = (
f"Bedrock long-term API key {api_key.id} in user "
f"{api_key.user.name} has already expired and can no longer "
f"authenticate."
)
elif (api_key.expiration_date - now).days > NEVER_EXPIRES_THRESHOLD_DAYS:
report.status = "FAIL"
report.check_metadata.Severity = Severity.critical
report.status_extended = (
f"Bedrock long-term API key {api_key.id} in user "
f"{api_key.user.name} is configured to never expire. Use "
f"short-term Bedrock API keys (session-scoped, valid up to "
f"12 hours) for non-exploratory workloads instead."
)
else:
days_until_expiration = (api_key.expiration_date - now).days
report.status = "FAIL"
report.status_extended = (
f"Bedrock long-term API key {api_key.id} in user "
f"{api_key.user.name} is active and will expire in "
f"{days_until_expiration} days. Use short-term Bedrock API "
f"keys (session-scoped, valid up to 12 hours) for "
f"non-exploratory workloads instead."
)
findings.append(report)
return findingsReferences
- https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys.html
- https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys-generate.html
- https://docs.aws.amazon.com/IAM/latest/UserGuide/security-creds-programmatic-access.html#security-creds-alternatives-to-long-term-access-keys
- https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#rotate-credentials