Multi-factor Authentication (MFA) is an easy way to enhance the security of your AWS cloud architecture with minimal work. It takes two minutes to enable, and minimizes the possibility of an account being compromised by tenfolds. This post and the associated video explores MFA in AWS and how to use it to control access to critical parts of your cloud architecture
Single factor vs Multi factor
The “multi” in MFA is as opposed to “single” factor authentication. These “factors” are,
- Something you know - a password, a pin
- Something you have - a token of some sort you have with you, yubikeys, RSA keys etc (not you SMS!!, (technically yes, SMS is something you have, but on a different topic, it has been proven many times to be easily crackable))
- Something you are - a biometric detail, like a fingerprint or face scan
If you only use a single factor out of the above, it’s “single factor authentication”. However, in practice, single factor authentication usually points to just using a password, i.e. something you know. In my belief, “passwordless” methods like sending a one time link or a password to an email account is something you have because you have access to a place that you have. How you got access there is another matter.
MFA, makes use of 2 or more factors to authenticate a user. This is usually done as a combination of “something that you know” and “something that you have”, but this is not the only pattern.
The term Two-factor Authentication or 2FA is basically the same, except for fact that it only allows two, and exactly two factors. In my mind, MFA is the term, and 2FA is to be left behind.
Unless you took a big break from the Internet in late 2000s, you might have already used MFA in one or more online services you use. Most social media services enforce, or at least encourage MFA. If you’ve been working with AWS for even a few days, you might have seen the “No hardware MFA” warning in some Root accounts (which is a different topic altogether in some sense). In short, MFA has been around for sometime, and it has proven to be an effective way to avoid being compromised for most cases.
The point of MFA is to increase the number of data points that a user should use to authenticate themselves. This is under the assumption that the difficulty of compromising two data points (two factors) of the same user is order of magnitude higher than the difficulty of compromising just a single data point.
There are some nuances here when biometrics are involved in some legal frameworks, but let’s be honest. We are not worried about a your own state coming after you.
We are worried about scenarios ranging from a teenager who learnt scamming recently trying out passwords he bought off a market place so that he can get some money to gift designer handbags to random girls at the nightclub, to a sophisticated spear-phishing campaign done by groups that may or may not be enemy state actors, who are definitely not trying to increase their GDP or fund invasions.
In most of these cases, a strong, unique password, paired with a secure MFA token, and sensible practices like using a password manager, and being cool headed (I admit is probably easier to say than do) could easily deter the attack.
MFA Standards
There are various standards out there, but mostly FIDO U2F
and OATH-TOTP
are
important to us. This is because, these two are the main standards used by AWS
in MFA. FIDO-U2F is used in AWS Web Console MFA authentication if you are using
something like a Yubikey, and OATH-TOTP is used when providing MFA details in the
CLI (if you’re using a virtual MFA device, only OATH-TOTP is involved).
I went into details about FIDO-U2F and OATH-TOTP usage in AWS in my previous post about enabling Yubikeys for AWS CLI and API operations
OATH-TOTP is a TOTP (Time based One Time Password) protocol, where a pre-shared secret is used along with the current time to generate a reproducible token. Since both parties have this information, if the pre-shared key didn’t get compromised, and if there’s no major time error in either of the devices, the resulting token should be the same, and should act as “something that you have”.
The pre-shared key is usually a base32 encoded string, which is usually shared as a QR code. But it doesn’t have to be. As long as you can read the base32 string and type it down, you can have a desktop application generate the tokens for you. Major password managers provide the functionality where ALthe MFA token can also be generated by it (in my opinion, this is not advisable, because it provides a single place where both factors can be siphoned out, but there are arguments out there that are on the opposite side).
FIDO-U2F on the other hand makes use of Public Key Cryptography, where the public keys of the user and the service are exchanged. The tokens are then generated and signed with the self public keys which are cryptographically secure to provide authentication function.
Compared to OATH-TOTP, FIDO-U2F provides verification of the service in addition to authentication. This is a huge improvement from OATH-TOTP which is vulnerable to phishing attacks, where a user could input the correct token to an incorrect interface. There is no computational guarantee that a user will not be tricked to expose the token.
In fact, a few of the most recent cryptocurrency wallet hacks have been facilitated by this kind of non-PKI backed MFA methods (sources for these I can’t find unfortunately, but if you have been keeping an ear on that subject, you might be able to think of a few scenarios). The usual story is Google or Facebook “support” calling you up to “verify that you are in fact the owner” to “block a current hacking attempt” where they trigger the MFA sequence to siphon the token off you. FIDO-U2F only goes through if the public key registered for the service matches the public key provided by the service that is asking for the token.
Passkeys is also a protocol based on FIDO-U2F
MFA in AWS
AWS supports MFA for IAM users and built-in SSO (IAM Identity Center) user store.
Both these services can have users from federated identity providers, so MFA for those users is a matter for those external identity providers to implement.
It takes 2 minutes to enable MFA on both these places.
MFA for IAM Users
In IAM, the place to enable MFA is in the “Security Credentials” tab for the user. There, select “Assign MFA Device”.
AWS provides three options to select as an MFA device.
- Virtual Device - phone (or desktop) applications such as Aegis, or Google Authenticator
- Security Key - FIDO-U2F enabled key like Yubikey
- Hardware TOTP token - older RSA keys that used to be around about 10 years ago
“Fun Fact”, if you are using Google Authenticator, and if you’re backing it to the Cloud (AFAIK this is enabled by default), you’re breaching MFA, and anyone that gets access to your email immediately gets access to all the MFA tokens for any other service you might be using.
Don’t use Google Authenticator! I have had success using Aegis, and Open Source application that also provides the capability to backup the vault with a password.
Virtual MFA
This uses the OATH-TOTP method I talked about, and provides the usual method of sharing the PSK through a QR code or the base32 encoded string. Scan the QR code with your virtual MFA application (or copy the base32 string to your app), and provide two consecutive tokens generated afterwards, to finish the process.
Security Key
Before selecting this option, or better, before buying a Yubikey, make sure it supports both FIDO-U2F and OATH-TOTP. The intersection of these two features cover only a limited set of devices.
Also, Security Keys that use FIDO-U2F and WebAuthn standards has a limit of “slots” that can be used to store external service public keys. So this is essentially a limit on the number of relying parties you can use with (ex:) a single Yubikey. This is a direct disadvantage of security keys.
Always try to buy security keys in pairs, and register both of them in the services you use. This way, if you lose the main physical device, you have a backup device to avoid getting locked out of your accounts.
Select the “Security Key”
In the next screen, connect your Yubikey and activate it.
MFA for IAM Identity Center Users
The steps are more or less the same for IAM Identity Center. The place to start is in the “MFA Devices” tab in the user screen.
In IAM Identity Center however, the hardware token option is not there. This is probably a product design decision than anything else, one which I can support.
The steps are the same as in IAM users after this.
MFA for API Calls
Now that MFA is enabled, whenever you login to AWS console, you’d get prompted to add the MFA token. This secures the console.
But what about the API calls?
Usually, when you access the AWS API with security credentials (either short-term credentials through AWS STS or long-term credentials through AWS IAM), your credentials will not be MFA authenticated.
If you take a look at the SessionContext in CloudTrail for an API call you make without MFA, you’d see mfaAuthenticated
set to false
.
To get this to show up as true, we’d need to satisfy a few conditions.
- Short-term credentials should be used. MFA authentication doesn’t apply to long term credentials as they don’t have an expiry
- Short-term credentials need to be generated with valid MFA details attached (kinda obvious)
To satisfy these conditions, we can use AWS STS GetSessionToken
API operation.
GetSessionToken
and AssumeRole
are the only two AWS STS operations that allow specifying MFA details in the request (in contrast to other operations like AssumeRoleWithWebIdentity
).
However AssumeRole
response doesn’t include MFA auth details (so it doesn’t satisfy condition #2 above), and we have to use GetSessionToken
.
Based on this, you can write AWS resource or identity policies to limit access to critical parts of your cloud architecture unless the credentials are generated with MFA involved. You can say if the request context doesn’t have MFA authenticated don’t allow certain API actions.
Let’s take a look at a sample.
I’m going to restrict GetObject
action in an S3 bucket if the session context doesn’t have MFA authentication details.
The condition I am using here is aws:MultiFactorAuthPresent
.
I’m denying GetObject
if this is not present or is present and set to false (BoolIfExists
behaviour).
Now I’m going to try to download this file with,
- long term credentials
- short-term credentials from an
AssumeRole
operation - short-term credentials from a
GetSessionToken
operation
For API calls, AWS uses OATH-TOTP based MFA tokens, NOT FIDO-U2F if you’re using a Security Key as your MFA device. My blog post that I linked above (and the video) on Yubikey usage goes into detail about it.
If your MFA device is a Virtual Device, you don’t have to worry about this. You’re already using OATH-TOTP for both the Web Console MFA and the API call MFA.
Testing with Long Term Credentials without MFA
My first attempt is to try and access this file through long term credentials. They
don’t have a SessionContext
and in turn, no MFA auth details attached in the request context.
So this gets denied!
Testing Short Term Credentials from an AssumeRole Operation
Then I’ll try and access this through IAM identity center short term credentials which are basically results of an AssumeRole
operation.
The request context will have a SessionContext
object, but mfaAuthenticated
will be set to false.
This is true even though my SSO user has an MFA device attached to it. This is because AssumeRole
response doesn’t have MFA auth details attached to it.
So this gets denied!
IMPORTANT: You might be horrified by the fact that I’m exposing quite a few details in these screenshots. These accounts were sandbox accounts, and they are not active anymore. Don’t look at my screenhots and be “emboldened” to blatantly post CloudTrail logs in public. They can contain highly sensitive details.
Do as I say, not as I do. Please, and thank you!
Even if I try to get temporary credentials from an AssumeRole
operation in
the CLI, I’d still be getting the same response.
The result of the operation run in a session with the above temporary credentials is the same, DENIED!
Testing Short Term Credentials from a GetSessionToken Operation
Now I’m going to generate some temporary credentials with GetSessionToken
operation.
You can generate temporary credentials without MFA with GetSessionToken
even if the IAM user has an MFA device registered.
The response will just not have MFA authentication details attached to it
In this case, I’m specifying the MFA details by using two arguments, --serial-number
and --token-code
.
--serial-number
is the ARN generated for the MFA device when you registered
it in either IAM or IAM Identity Center (depending on which user you’re using
for GetSessionToken
).
--token-code
will be the OATH-TOTP token generated by the virtual device or
the security key.
The resulting response will be valid for 15 minutes and will have MFA auth details attached
When I use these short term credentials to establish a session and download the object, it goes through!
This is because these credentials satisfied the two conditions above, short-term, and generated with MFA details attached.
Let’s take a look at the request context.
mfaAuthenticated
is set to true
and our request is allowed.