Skip to main content

IAM User Hardware MFA Enabled

Overview

This check verifies that IAM users have hardware-based multi-factor authentication (MFA) enabled. Hardware MFA devices include physical security keys (FIDO2/U2F) and hardware TOTP tokens. These are more secure than virtual authenticator apps or SMS-based MFA.

Risk

Without hardware MFA, your accounts are vulnerable to:

  • Phishing attacks that can intercept codes from virtual authenticator apps
  • SIM-swap attacks that compromise SMS-based verification
  • Credential theft where passwords alone grant full access

A compromised IAM user account can lead to data breaches, unauthorized resource access, and privilege escalation throughout your AWS environment.

Severity: High

Remediation Steps

Prerequisites

You need:

  • AWS Console access with IAM administrative permissions
  • A hardware MFA device (FIDO2 security key like YubiKey, or a hardware TOTP token)
Supported hardware MFA devices

AWS supports two types of hardware MFA:

FIDO2 Security Keys (Recommended)

  • YubiKey 5 Series
  • Feitian ePass FIDO
  • Other FIDO2-certified security keys

Hardware TOTP Tokens

  • Gemalto tokens
  • Other time-based one-time password hardware devices

FIDO2 keys are preferred because they are phishing-resistant - they verify the website domain before responding.

AWS Console Method

  1. Sign in to the AWS Console
  2. Go to IAM (search for "IAM" in the top search bar)
  3. Click Users in the left sidebar
  4. Click on the username you want to secure
  5. Select the Security credentials tab
  6. Scroll to Multi-factor authentication (MFA)
  7. If a virtual MFA device is already assigned, click Remove to deactivate it first
  8. Click Assign MFA device
  9. Enter a friendly device name (e.g., "MyYubiKey")
  10. Select either:
    • Security key for FIDO2 devices (YubiKey, etc.)
    • Hardware TOTP token for physical code generators
  11. Follow the on-screen prompts:
    • For security keys: Insert the key and touch it when prompted
    • For TOTP tokens: Enter the serial number and two consecutive codes
  12. Click Add MFA
AWS CLI (optional)

List current MFA devices for a user:

aws iam list-mfa-devices \
--user-name <username> \
--region us-east-1

Check for virtual MFA devices in your account:

aws iam list-virtual-mfa-devices \
--assignment-status Assigned \
--region us-east-1

Deactivate an existing MFA device (if replacing):

aws iam deactivate-mfa-device \
--user-name <username> \
--serial-number <mfa-device-arn> \
--region us-east-1

Enable a hardware TOTP MFA device:

aws iam enable-mfa-device \
--user-name <username> \
--serial-number <hardware-token-serial-number> \
--authentication-code1 <code1> \
--authentication-code2 <code2> \
--region us-east-1

Replace:

  • <username> with the IAM user's name
  • <mfa-device-arn> with the ARN of the device to remove
  • <hardware-token-serial-number> with the serial number printed on your hardware token
  • <code1> and <code2> with two consecutive 6-digit codes from the device

Note: FIDO2 security keys must be registered through the AWS Console - they cannot be enabled via CLI.

CloudFormation (optional)

CloudFormation cannot directly assign MFA devices to IAM users. MFA device registration requires interactive user input (touching a security key or entering codes from a token).

However, you can enforce MFA requirements through IAM policies. Here is an example policy that denies most actions unless MFA is present:

AWSTemplateFormatVersion: '2010-09-09'
Description: IAM policy requiring MFA for sensitive actions

Resources:
RequireMFAPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: RequireHardwareMFA
Description: Denies access to sensitive actions without MFA
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AllowViewAccountInfo
Effect: Allow
Action:
- iam:GetAccountPasswordPolicy
- iam:ListVirtualMFADevices
- iam:ListMFADevices
Resource: '*'
- Sid: AllowManageOwnMFA
Effect: Allow
Action:
- iam:CreateVirtualMFADevice
- iam:DeleteVirtualMFADevice
- iam:EnableMFADevice
- iam:ResyncMFADevice
Resource:
- !Sub 'arn:aws:iam::${AWS::AccountId}:mfa/${!aws:username}'
- !Sub 'arn:aws:iam::${AWS::AccountId}:user/${!aws:username}'
- Sid: DenyAllExceptListedIfNoMFA
Effect: Deny
NotAction:
- iam:CreateVirtualMFADevice
- iam:EnableMFADevice
- iam:GetUser
- iam:ListMFADevices
- iam:ListVirtualMFADevices
- iam:ResyncMFADevice
- sts:GetSessionToken
Resource: '*'
Condition:
BoolIfExists:
'aws:MultiFactorAuthPresent': 'false'

Deploy with:

aws cloudformation deploy \
--template-file require-mfa-policy.yaml \
--stack-name require-mfa-policy \
--capabilities CAPABILITY_NAMED_IAM \
--region us-east-1
Terraform (optional)

Like CloudFormation, Terraform cannot directly assign MFA devices. You can enforce MFA requirements through policy:

# MFA enforcement policy
resource "aws_iam_policy" "require_mfa" {
name = "RequireHardwareMFA"
description = "Denies access to sensitive actions without MFA"

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowViewAccountInfo"
Effect = "Allow"
Action = [
"iam:GetAccountPasswordPolicy",
"iam:ListVirtualMFADevices",
"iam:ListMFADevices"
]
Resource = "*"
},
{
Sid = "AllowManageOwnMFA"
Effect = "Allow"
Action = [
"iam:CreateVirtualMFADevice",
"iam:DeleteVirtualMFADevice",
"iam:EnableMFADevice",
"iam:ResyncMFADevice"
]
Resource = [
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:mfa/$${aws:username}",
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/$${aws:username}"
]
},
{
Sid = "DenyAllExceptListedIfNoMFA"
Effect = "Deny"
NotAction = [
"iam:CreateVirtualMFADevice",
"iam:EnableMFADevice",
"iam:GetUser",
"iam:ListMFADevices",
"iam:ListVirtualMFADevices",
"iam:ResyncMFADevice",
"sts:GetSessionToken"
]
Resource = "*"
Condition = {
BoolIfExists = {
"aws:MultiFactorAuthPresent" = "false"
}
}
}
]
})
}

data "aws_caller_identity" "current" {}

# Attach to a user or group
resource "aws_iam_user_policy_attachment" "require_mfa" {
user = "<username>"
policy_arn = aws_iam_policy.require_mfa.arn
}

Verification

After assigning the hardware MFA device:

  1. In the IAM Console, go to Users > select the user > Security credentials
  2. Under Multi-factor authentication (MFA), verify the device shows as assigned
  3. The device type should indicate "Hardware" or "Security key" (not "Virtual")
CLI verification
aws iam list-mfa-devices \
--user-name <username> \
--region us-east-1

The response should show a device with a serial number that does NOT start with arn:aws:iam::. Virtual MFA devices have ARN-style serial numbers, while hardware devices have physical serial numbers.

Example hardware MFA response:

{
"MFADevices": [
{
"UserName": "alice",
"SerialNumber": "GAHT12345678",
"EnableDate": "2024-01-15T10:30:00Z"
}
]
}

Example virtual MFA response (what you do NOT want):

{
"MFADevices": [
{
"UserName": "alice",
"SerialNumber": "arn:aws:iam::123456789012:mfa/alice",
"EnableDate": "2024-01-15T10:30:00Z"
}
]
}

Additional Resources

Notes

  • FIDO2 security keys are the most secure option - they are phishing-resistant because they verify the website domain before responding
  • Hardware TOTP tokens require entering two consecutive codes during setup to synchronize timing
  • If replacing an existing virtual MFA device, the user may temporarily lose access until the hardware device is registered - coordinate the transition
  • For programmatic access (CLI/SDK), users must call sts:GetSessionToken with their MFA code to obtain temporary credentials
  • Consider enforcing hardware MFA organization-wide using AWS Organizations Service Control Policies (SCPs)