Back to Checks
aws

IAM user has hardware MFA enabled

iam_user_hardware_mfa_enabled
Service: iamSeverity: high
OfficialBuilt-inVerified
Categories:identity-access
Resource Type:AwsIamUser

IAM users are evaluated for hardware MFA enrollment, identifying physical tokens or security keys and distinguishing them from virtual or SMS MFA, as well as users without any MFA.

Risk

Without hardware MFA, authentication is weaker:

  • SIM-swap can bypass SMS
  • Phishing can steal TOTP from virtual apps
  • No MFA allows password-only takeover This enables unauthorized console/API access, causing data exfiltration (C), privilege abuse (I), and service disruption (A).

Run this check with Prowler

Run in Prowler Cloud
Run
prowler aws --checks iam_user_hardware_mfa_enabled

Recommendation

Require hardware-backed MFA for all IAM users. Prefer FIDO2 security keys for phishing resistance over TOTP or SMS. Disallow SMS/virtual MFA for privileged roles. Enforce MFA for all access paths, apply least privilege, and provision multiple MFA devices per user for continuity.

Remediation

Other

1Sign in to the AWS Console and open IAM
2Go to Users > select <user> > Security credentials
3Under Multi-factor authentication (MFA), if a Virtual MFA device or SMS MFA is listed, choose Deactivate/Remove and confirm
4Click Assign MFA device
5Select Hardware TOTP token or Security key (FIDO2) and choose Next
6For Hardware TOTP: enter the device serial, then enter MFA code 1 and MFA code 2 from the token; for Security key: insert/tap the key and follow the prompts
7Choose Add/Save to complete

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_hardware_mfa_enabled(Check):
    def execute(self) -> Check_Report_AWS:
        findings = []
        response = iam_client.users

        for user in response:
            report = Check_Report_AWS(metadata=self.metadata(), resource=user)
            report.region = iam_client.region
            if user.mfa_devices:
                report.status = "PASS"
                report.status_extended = f"User {user.name} has hardware MFA enabled."
                for mfa_device in user.mfa_devices:
                    if mfa_device.type == "mfa" or mfa_device.type == "sms-mfa":
                        report.status = "FAIL"
                        report.status_extended = f"User {user.name} has a virtual MFA instead of a hardware MFA device enabled."
                        break

                findings.append(report)
            else:
                report.status = "FAIL"
                report.status_extended = (
                    f"User {user.name} does not have any type of MFA enabled."
                )
                findings.append(report)

        return findings