The AWS account administrator's guide to MFA

How to harden user accounts with a second factor and enforce its usage

Author's image
Tamás Sallai
9 mins

Multi-factor authentication adds another requirement for users to access their accounts. People are terrible at choosing a secure password, but they are excellent to spot when a physical thing is missing. MFA builds on this strength by requiring something you have (a phone or a device) as well as something you know (the password).

You can use MFA throughout the AWS account. You can add a device to the root account as well as to individual IAM users. As an administrator, you can monitor that users have their devices enabled, as well as enforce their usage to access protected resources in an account.

This article discusses what are the options for MFA in AWS, how to check and enforce its usage across the users, and how to handle a lost device.

MFA setup

In AWS, each IAM user and the root user can have an MFA device. When there is one associated with the user, signing in to the Console requires using it.

MFA devices can be TOTP-based and U2F. They are fundamentally different, so let's see each of them!

TOTP-based MFA

TOTP is short for Time-based One-time Password, which is a device or an app that shows a 6-digit number every 30 seconds. When you log in, you need to input the numbers you see in addition to the password.

Authy screenshot

(Image from https://authy.com/blog/authy-onetouch-simply-strong-security/)

It works by having a secret key shared between the device and the server, and a synchronized clock. The digits you see on the device are the hash of the shared secret and the current value of the clock rounded to 30 seconds. Since both of these data are present on both sides the server can verify the digits.

When you add an MFA device to an AWS account, you need to get the secret key to both sides. It is more apparent when adding a virtual device:

TOTP secret

Note the secret key at the bottom. The QR code is just a convenient way to transfer this to the phone as many apps allow reading these codes.

Authentication works by using this secret and the clock:

Virtual TOTP

The easiest way to start using MFA is to use a virtual token, which is usually an app that runs on your phone. After synchronizing the secret to the app it generates the digits you can use to log in. The second factor is your phone in this case.

There are several apps to choose from. Google Authenticator is probably the best-known, but there is Authy, LastPass Authenticator, andOTP, and even web-based ones, such as totp.app.

Note that technically it is still something you know, the shared secret, and it can be recovered from your phone. This has advantages, such as the ability to backup the codes to another device or the cloud, but comes with a slight compromise on the security. If an attacker has access to your phone he can extract the codes and later use them to sign in to your account.

Hardware TOTP

To make sure a TOTP secret can not be copied you can buy dedicated hardware. This is manufactured with the secret included so there is no need to transfer it to the device. All you need to do is to type the serial number and two codes, and the device is associated with your account.

Hardware TOTP token

(Image from https://cpl.thalesgroup.com/access-management/authenticators/safenet-otp-110)

These devices are tamper-proof to some extent but also tamper-evident, meaning it's hard to extract the secret but you'll also notice that someone tried to do it. But because of this, contrary to virtual devices, there is no way to backup it. If you lose it or break it, it's gone for good.

Notice that these devices come with a battery and they are not internet-connected. The secret is preloaded and they have a clock and their only connection with the outside world is through their LCD screen. Sometimes their clocks can drift, meaning the timestamp is different on the device than the server. You can resynchronize the device in that case. Reading the comments, it's a surprisingly common problem.

While every virtual TOTP works with every service supporting TOTP, it's not true for hardware tokens. Since the secret is stored on the manufacturer's servers, the service needs to contact them to check the token. Make sure to check which models AWS supports before you buy the tokens.

U2F MFA

U2F is based on a challenge-response authentication using private and public keys generated on the device. Because of this, an active connection is needed between the device and the server.

U2F MFA flow

(Image from https://www.yubico.com/blog/yubicos-u2f-key-wrapping/)

From a technical perspective, this is a superior solution to the TOTP tokens. But there is no way to backup it, and it needs a connection and that always makes things more complicated.

Using it is straightforward, just press the button. It even comes integrated into some laptops. U2F is universal, so every token work with every service.

One AWS-specific downside is that you can not use it with the CLI, only to sign in to the Console.

Check MFA usage

When it comes to administering IAM users you can look at the IAM console to see if they have MFA enabled:

The type of the MFA device is shown on the IAM Console

This list also shows whether they are using virtual or hardware tokens.

The Credential report that you can download also contains MFA usage, but only in a true/false way.

Enforce MFA

You can enforce the usage of MFA with IAM policies. To require it, use the "Bool": {"aws:MultiFactorAuthPresent": "true"} condition. You can add this to Allowed statements to make sure they are only allowed with MFA:

"Statement": [
	{
		"Effect": "Allow",
		"Action": [
			"s3:DeleteObject"
		],
		"Resource": ["arn:aws:s3:::ACCOUNT-A-BUCKET-NAME/*"],
		"Condition": {
			"Bool": {
				"aws:MultiFactorAuthPresent": "true"
			}
		}
	}
]

Or you can Deny operations not listed when MFA is missing:

{
	"Effect": "Deny",
	"NotAction": [
		"iam:ListMFADevices",
		"sts:GetSessionToken",
		"sts:GetCallerIdentity"
	],
	"Resource": "*",
	"Condition": {
		"BoolIfExists": {
			"aws:MultiFactorAuthPresent": "false"
		}
	}
}

Of course, you can use this condition on resource-based policies too. This prevents deleting objects without MFA:

{
	"Effect": "Deny",
	"Principal": "*",
	"Action": "s3:DeleteObject",
	"Resource": "arn:aws:s3:::<bucket>/*",
	"Condition": {
		"BoolIfExists": {
			"aws:MultiFactorAuthPresent": "false"
		}
	}
}

Using IAM policies to enforce MFA usage not only makes sure users add MFA devices to their accounts but also limits what they can do with the CLI. When MFA is enabled, it prevents signing in to the Console, but it does not affect access keys. By denying actions when MFA is not used you can make sure the device has to be used for all operations be it via the Console or the APIs.

Unfortunately, there is no way to distinguish between different types of tokens. If you distribute hardware tokens to users but they decide to replace it with a virtual one you can only detect it using the IAM console but can not make the policies deny access in that case.

Allow users to set up MFA

Since it's best practice to let users set up their passwords, it's also a good measure to allow them to add their own MFA device.

The AWS documentation has a fairly lengthy policy how to allow a user to add an MFA device and also denying everything that is not related to that without MFA auth.

{
	"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"}
	}
}

When you allow a user to set up an MFA device you need to allow certain operations without MFA. This is because when you create the user there is no device associated with it. But you must not add the ability to remove a device without MFA authentication. This would allow an attacker to remove the user's device and add his own instead.

CLI access

MFA protects the Console login by default, and the above policy also prevents any action without using the second factor. But since API access is based on long-lived access tokens, how to use MFA with them?

You can use the aws sts get-session-token call which supports the --serial-number <arn> and the --token-code <value> arguments and returns a temporary credentials set. You can use these keys to interact with the AWS APIs where MFA protection is needed.

Cross-account access

Cross-account access is done by creating a role that trusts the other account. This makes sure that you don't need to exchange secrets (passwords or access keys), only the account ID, and, optionally, an external ID.

You can make the policy stricter by requiring MFA authentication for the user who is assuming the cross-account role.

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Principal": {"AWS": "<account id>"},
			"Action": "sts:AssumeRole",
			"Condition": {
				"Bool": {
					"aws:MultiFactorAuthPresent": true
				}
			}
		}
	]
}

This helps, but it's not the same degree of security as requiring MFA in the account you manage. This policy requires MFA authentication, but you have no control over the device that is used. If there is a vulnerability in the trusted account where an attacker can replace the MFA device with his own then he can use this role with that.

What if the device is lost

The Achilles' heel of MFA is what happens when the user loses the device. For some services, it is a matter of contacting the support, which defeats the security benefits, and on the other end of the spectrum, you can lose access to the service.

IAM users can usually go to an account admin and ask for a replacement. This makes it just a matter of some inconvenience, as there is always a user above them.

If the affected account is the root account AWS has a process to recover the account in this case.

Conclusion

Adding MFA to an IAM user greatly enhances its security and enforcing MFA usage across an account helps when users are targeted by an attack. You, as an administrator, can list who is using a second factor and you can also enforce its usage through IAM policies.

October 2, 2020