Back to Checks
aws

Root account has a hardware MFA device enabled

iam_root_hardware_mfa_enabled
Service: iamSeverity: critical
OfficialBuilt-inVerified
Categories:identity-access
Resource Type:AwsIamUser

AWS root user credentials are assessed for MFA status and device type. The check detects whether MFA is absent or implemented with a virtual device instead of hardware MFA on the root user, and notes when centralized root credential management is in effect.

Risk

Without hardware MFA on the root user:

  • No MFA: stolen password/keys enable full account takeover.
  • Virtual MFA: device compromise or backup restoration weakens second-factor assurance. An attacker could delete resources, change policies, and disable logging, harming confidentiality, integrity, and availability.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler aws --checks iam_root_hardware_mfa_enabled

Recommendation

Require a hardware MFA token for the root user and remove any virtual MFA. Apply least privilege: avoid using root, disable access keys, and eliminate long-term credentials. In organizations, centralize root management. Keep a controlled break-glass process with strict recovery checks and continuous monitoring.

Remediation

Other

1Sign in to the AWS Management Console as the root user
2Open My Security Credentials: https://console.aws.amazon.com/iam/home?#/security_credentials
3In the Multi-factor authentication (MFA) section, choose Activate/Assign MFA
4Select a hardware option (Security key or Hardware TOTP token) and complete the prompts (for TOTP: enter the device serial and two consecutive codes)
5After the hardware MFA is added, locate any Virtual MFA device listed for root and Deactivate/Remove it
6Confirm only the hardware MFA remains assigned

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_root_hardware_mfa_enabled(Check):
    def execute(self) -> Check_Report_AWS:
        findings = []
        # This check is only available in Commercial Partition
        if iam_client.audited_partition == "aws":
            if iam_client.credential_report:
                for user in iam_client.credential_report:
                    if user["user"] == "<root_account>":
                        password_enabled = user["password_enabled"] == "true"
                        access_key_1_active = user["access_key_1_active"] == "true"
                        access_key_2_active = user["access_key_2_active"] == "true"

                        # Only report if root actually has credentials
                        if (
                            password_enabled
                            or access_key_1_active
                            or access_key_2_active
                        ) and iam_client.account_summary:
                            virtual_mfa = False
                            report = Check_Report_AWS(
                                metadata=self.metadata(),
                                resource=user,
                            )
                            report.region = iam_client.region
                            report.resource_id = user["user"]
                            report.resource_arn = iam_client.mfa_arn_template

                            # Check if organization manages root credentials
                            org_managed = (
                                iam_client.organization_features is not None
                                and "RootCredentialsManagement"
                                in iam_client.organization_features
                            )

                            if (
                                iam_client.account_summary["SummaryMap"][
                                    "AccountMFAEnabled"
                                ]
                                > 0
                            ):
                                for mfa in iam_client.virtual_mfa_devices:
                                    # If the ARN of the associated IAM user of the Virtual MFA device is "arn:aws:iam::[aws-account-id]:root", your AWS root account is not using a hardware-based MFA device for MFA protection.
                                    if "root" in mfa.get("User", {}).get("Arn", ""):
                                        virtual_mfa = True
                                        report.status = "FAIL"
                                        if org_managed:
                                            report.status_extended = (
                                                "Root account has credentials with virtual MFA "
                                                "instead of hardware MFA despite organizational root management being enabled."
                                            )
                                        else:
                                            report.status_extended = "Root account has a virtual MFA instead of a hardware MFA device enabled."
                                        break

                                if not virtual_mfa:
                                    report.status = "PASS"
                                    if org_managed:
                                        report.status_extended = (
                                            "Root account has credentials with hardware MFA enabled. "
                                            "Consider removing individual root credentials since organizational "
                                            "root management is active."
                                        )
                                    else:
                                        report.status_extended = "Root account has a hardware MFA device enabled."
                            else:
                                report.status = "FAIL"
                                if org_managed:
                                    report.status_extended = (
                                        "Root account has credentials without MFA "
                                        "despite organizational root management being enabled."
                                    )
                                else:
                                    report.status_extended = (
                                        "MFA is not enabled for root account."
                                    )

                            findings.append(report)

        return findings