Back to Checks
oraclecloud

User active API key is rotated within 90 days or less

identity_user_api_keys_rotated_90_days
Service: identitySeverity: medium
OfficialBuilt-inVerified
Categories:identity-accesssecrets
Resource Type:User

OCI IAM users with active API signing keys older than 90 days are identified. Key age is derived from each key's creation time; only active keys are considered. Users without API keys are recorded.

Risk

Long-lived API keys widen exposure. If a key leaks, an attacker can sign OCI API calls without MFA, enabling unauthorized changes (integrity), data access (confidentiality), and service outages (availability). Delayed rotation prolongs dwell time and complicates incident response.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler oraclecloud --checks identity_user_api_keys_rotated_90_days

Recommendation

Enforce API key rotation every 90 days.

  • Issue a new key, confirm workloads use it, then revoke the old key
  • Apply least privilege and avoid shared keys
  • Limit active keys per user and remove unused ones
  • Monitor usage and automate rotation for defense in depth

Remediation

CLI

oci iam api-key delete --user-id <user-ocid> --fingerprint <old-key-fingerprint> --force

Other

1Sign in to the OCI Console
2Go to Identity & Security > Users, then select the target user
3Open API Keys and click Add API Key
4Generate API Key Pair (or Upload/Paste Public Key), then click Add and download/copy the private key
5For each API key older than 90 days, click the Actions (three dots) next to its fingerprint and select Delete
6Confirm deletion

Check Code

"""Check if user API keys rotate within 90 days or less."""

from datetime import datetime, timezone

import pytz

from prowler.lib.check.models import Check, Check_Report_OCI
from prowler.providers.oraclecloud.services.identity.identity_client import (
    identity_client,
)

maximum_expiration_days = 90


class identity_user_api_keys_rotated_90_days(Check):
    """Check if user API keys rotate within 90 days or less."""

    def execute(self) -> Check_Report_OCI:
        """Execute the identity_user_api_keys_rotated_90_days check.

        Returns:
            List of Check_Report_OCI objects with findings
        """
        findings = []

        for user in identity_client.users:
            # Check if user has API keys
            if user.api_keys:
                for api_key in user.api_keys:
                    # Only check active API keys
                    if api_key.lifecycle_state == "ACTIVE":
                        report = Check_Report_OCI(
                            metadata=self.metadata(),
                            resource=user,
                            region=user.region,
                            resource_name=user.name,
                            resource_id=user.id,
                            compartment_id=user.compartment_id,
                        )

                        # Calculate age of the API key
                        now = datetime.now(timezone.utc)
                        # Ensure api_key.time_created is timezone aware
                        if api_key.time_created.tzinfo is None:
                            key_created = api_key.time_created.replace(tzinfo=pytz.utc)
                        else:
                            key_created = api_key.time_created

                        age_days = (now - key_created).days

                        if age_days > maximum_expiration_days:
                            report.status = "FAIL"
                            report.status_extended = f"User {user.name} has API key (fingerprint: {api_key.fingerprint[:16]}...) that has not been rotated in {age_days} days (over 90 days)."
                        else:
                            report.status = "PASS"
                            report.status_extended = f"User {user.name} has API key (fingerprint: {api_key.fingerprint[:16]}...) that was created {age_days} days ago (within 90 days)."

                        findings.append(report)
            else:
                # User has no API keys
                report = Check_Report_OCI(
                    metadata=self.metadata(),
                    resource=user,
                    region=user.region,
                    resource_name=user.name,
                    resource_id=user.id,
                    compartment_id=user.compartment_id,
                )
                report.status = "PASS"
                report.status_extended = f"User {user.name} does not have any API keys."
                findings.append(report)

        return findings