Full MFA Protection in AWS

Security best practices typically call for some form of multi-factor authentication when accessing sensitive information system, or when accessing systems with elevated privileges (admin level). In most cases, accessing a public cloud provider via WebConsole or API is accessing the system for administrative purposes and requires an extra level of protection than a simple password.

AWS Provides the ability to use MFA on it’s systems, but the ability to enforce that across all facets of the AWS API is not a trivial task. And as I’ve said before - the penultimate problem in modern information security is creating a system that’s easy enough to use that users will embrace the protection and not bypass it out of laziness or for expediency.

Last year I implemented requireMFA, a Lambda based CloudFormation stack that would prohibit users from accessing their normal granted permissions if they did not enable MFA in the AWS Console.

However the AWS console is only one attack vector against AWS. There are multiple methods of authentication AWS supports:

  • AWS Console with IAM Users
  • AWS Console with SAML Federation
  • API Access with long-term credentials
  • API Access with STS Generated temporary credentials

(There may be other less popular authentication methods involving Web Identity and Cognito Pools, but they’re not as frequent in corporate/enterprise settings. )

We’ve discussed AWS Console with IAM Users. That scenario is the easiest to setup MFA. And since you can’t always enforce good password behavior adding the second factor makes the most sense here.

SAML Federation could support a second-factor if the Identity Provider supports that. ADFS, Okta, and others can require the second factor before returning the SAML document to be provided to the AWS Console.

API Access with long-term credentials is a third way to authenticate to the cloud. It consists of a 20 character KEY_ID and a 40 character Secret. Neither are designed to be human remember-able so the potential for successful brute force is reduced. However, API Keys are no different from a password. Numerous cases abound of developers committing their keys into public source repositories and AWS accounts being compromised. I’m surprised we’ve not seen targeted malware that does a “cat ~/.aws/credentials”. 1

Finally, there are temporary credentials that are generated by the AWS STS service that expire after a defined period of time. These are similar to API Access keys, but include an additional session token. These can last from 15 minutes to 36 hours depending on the type of call.

What is AWS’s second factor?

AWS Supports two kinds of second-factor devices. Hardware Tokens or Time-based One-time Password. I’ve not used the hardware tokens (yet, but I have a reason to get one for work). The TOTP apps are now commonplace and used for AWS, Google, DropBox, GitHub and others. There are two major apps people use, both are for mobile devices. Google Authenticator is the most common. The problem is that there is no way to move your TOTP from one device to another. Authy is another TOTP mobile app. It allows you to create an account and can upload your TOTPs to the cloud for safe keeping. Unfortunately it uploads your TOTPs to the cloud where hackers or government goons with NSLs can get them. Pick your paranoia.

Back to the original question: Given all these different ways of authenticating and accessing AWS, how can Cloud Governance mandate 2FA for all AWS access?

For the AWS Console, implement the requireMFA Lambda. Deploying this solution, and moving all IAM users who have not enabled their MFA into the DenyUntilMFAEnabledGroup would protect the AWS Account holder from password compromises. It’s also important to set the AWS Password Policy

For SAML Federation, that will depend on your identity provider. Later versions of Microsoft’s ADFS support MFA. However in some cases there might be a heavy lift to upgrade Active Directory. Limiting ADFS access to a MFA protected internal network might be the solution in that case.

For API Access it is much harder. AWS IAM Policies do support a condition that multifactor is present. Leveraging this condition on all IAM Policies would provide a Cloud Governance mandate for 2FA on AWS Access. However there is a usability trade-off here. Users would need to maintain a set of long-term API Keys that would need to be used, in combination with their multi-factor device, to request short-term credentials that would then meet the MultiFactorAuthPresent policy condition.

This is what the IAM Policy attached to the User or Group would look like:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "*",
            "Resource": "*",
            "Condition": {
                "Bool": {
                    "aws:MultiFactorAuthPresent": "true"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": "sts:GetSessionToken",
            "Resource": "*"
        }
    ]
}

And this is the command you’d have to run to get 8 hr credentials (replace 123456789012, my-user-name and 123456 with the proper values):

aws sts get-session-token --duration-seconds 28800 \
  --serial-number arn:aws:iam::123456789012:mfa/my-user-name \
  --token-code 123456

In the policy there is a “aws:MultiFactorAuthPresent”: “true” requirement on their normal permissions, and that they are allowed to run sts:GetSessionToken without that condition. Permissions enabling them to setup MFA the first time would be provided by the DenyUntilMFAEnabledGroup deployed by requireMFA

There is a problem with this though: It requires you to abandon the use of AWS Managed Policies in favor of your own policies. Policies that contain exactly the same thing but with the Condition. That bumps it down on the usability scale.

This policy would permit the usage of AWS Managed Policies:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "NotAction": "sts:GetSessionToken",
            "Resource": "*",
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": false
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": "sts:GetSessionToken",
            "Resource": "*"
        }
    ]
}

However - the Deny NoAction will prevent the user from setting up their MFA, so you’ll want to tweak that with the contents from the DenyUntilMFAEnabledGroup policies.

Holy Cow - long post. But there is one more problem that needs to be discussed, and it’s the ugliest: SAML Federated API Keys.

It is possible, and in my opinion recommended, to use temporary credentials granted from SAML Federated authentication. This allows you to use your corporate Active Directory as the authoritative source for who is authorized to do what in your company. There are several systems out there that will perform authentication against your identity provider, then using the saml assertion, go and grab temp credentials from STS. There is a catch however: the one-hour hard limit on AssumeRoleWithSAML generated credentials. This is a hard limit in Amazon’s implementation and a frequent request of AWS to fix.

If the Identity Provider doing the authentication just wants a password, that can be cached and background process run to get credentials every hour. However, if your Identity Provider demands a TOTP then a human will have to enter the code every hour. Usability goes into the toilet. Users rebel. CISOs fight with Development Mangers. Cats and dogs living together. Mass Hysteria!!!

If only AWS would fix the 1 hour timeout. Then there could be harmony again. Entering your code once every eight-hours is a perfectly reasonable requirement. Tools like Terraform won’t break when credentials expire after creating a multi-AZ RDS.

So there are your options. MFA can be applied against all the common facets of AWS Access. Simple tools can be built to make the user experience better. AWS could make the experience much better and help foster better security.

1: This is why my API Keys are stored in the keychain, however I don’t hold many illusions that my keychain is much safer than a flat file on my laptop.