Outdated Default AWS IAM Policy Language Versions

February 16, 2023

Jason Kao
Name
Jason Kao
Twitter
kaojason

Overview

The AWS Identity and Access Management (IAM) service is used to securely control access to AWS resources and can be expressed in JSON as IAM policies (opens in a new tab). The Version element of the JSON policy specifies which language syntax rules are used when processing the policy.

There are 2 possible Version element values: 2012-10-17 and 2008-10-17. The 2008-10-17 version is an earlier version that does not support newer features such as policy variables and AWS recommends not to use this version for any new policies or when updating existing policies. (opens in a new tab) After going through all services that support IAM resource-based policies and identity-based policies, we found multiple AWS services that used the legacy 2008-10-17 policy version, including Gateway VPC Endpoint Policies, SNS Topic Policies, and SQS Queue Policies.

We sent details on our research and our thoughts regarding developer experience on outdated policy versions and default behavior to the AWS Security team on February 8th, 2023. This came up as a discussion point in an AWS Security online community regarding SNS Topic policies and we did further research on other default and example policies across all AWS services.

In this post, we will cover the IAM Policy Language Version and what that means, our research on which services have default legacy versions, the user experience in AWS when specifying the policy version, how to find default legacy versions in your AWS environment, and recommendations.

IAM Policy Language Version

A lesser known detail about IAM policy grammar is the policy Version element. This is not the different versions of IAM policies (opens in a new tab) that can be created for managed policies via CreatePolicyVersion. We're referring to the snippet of "Version": "2008-10-17" or "Version": "2012-10-17" that may be included in an IAM policy. This Version specifies which language syntax rules are used when processing the IAM policy.

In AWS console and even in the CLI/API, there are default and example policies that leverage the legacy interpretation of the IAM policy language which can create issues for developers. IAM is one of the building blocks in AWS and often can be a source of headaches for developers to properly configure permissions and access for applications, let alone focus on least privilege and properly defined IAM permissions. By using the legacy 2008-10-17 version of the policy language, this can lead to misconfiguration and development time spent troubleshooting differences in language syntax rules such as policy variables being treated as literal strings in the policy.

There are 3 different implicit and explicit settings for the Version element:

  • Explicit 2012-10-17
  • Explicit 2008-10-17
  • Implicit 2008-10-17: No Version specified.

The Version element can be present in both identity-based policies and resource-based policies (opens in a new tab) among other policy types.

  "Version": "2008-10-17",
  "Id": "OutdatedVersionElement",
  "Statement": [
    {
      "Sid": "ExampleStatement",
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": [
        "SQS:ReceiveMessage"]
    }
    ...

Identity-Based Policies and Their Versions

There are 4 types of identity-based policies that we will take a look at:

AWS has controls that block the creation of IAM managed policies with legacy policy versions. The following screenshots show the mechanisms for blocking a policy created without 2012-10-17 specified in the policy version. In console, an error message pops up about the version string.

AWS Console Message for Policy Version

With the AWS CLI, we get a `MalformedPolicyDocument`` error:

AWS CLI Error for Policy Version

However, these controls are inconsistent and do not apply to inline policies. Inline policies still can be created with legacy policy versions of 2008-10-17 or with no Version specified.

We used the following JSON as our policy:

{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Sid": "ListPolicies",
            "Effect": "Allow",
            "Action": [
                "iam:ListPolicies"
            ],
            "Resource": "*"
        }
    ]
}

The following command to put an inline policy on our cloudquery-user user succeeded:

aws iam put-user-policy \
    --user-name cloudquery-user \
    --policy-name test-cq-policy \
    --policy-document file://2008policyversion.json                      

Finding Identity-Based Policies with CloudQuery

The below queries have a prerequisite to use CloudQuery to sync AWS data to PostgreSQL. For a guide, see our quickstart guide (opens in a new tab).

We will use CloudQuery to check for identity-based with 2008-10-17 explicitly set as the version or for identity-based policies without an explicit version set.

Finding IAM User Inline Policies

SELECT * FROM
    (
        SELECT policy_document ->> 'Version' as version, *
        FROM aws_iam_user_policies   
    ) as user_policies
WHERE version = '2008-10-17' 
OR version is NULL;

Finding IAM Group Inline Policies

SELECT * FROM
    (
        SELECT policy_document ->> 'Version' as version, *
        FROM aws_iam_group_policies   
    ) as group_policies
WHERE version = '2008-10-17' 
OR version is NULL;

Finding IAM Role Inline Policies

SELECT * FROM
    (
        SELECT policy_document ->> 'Version' as version, *
        FROM aws_iam_role_policies   
    ) as role_policies
WHERE version = '2008-10-17' 
OR version is NULL; 

Resource-Based Policies and Their Versions

In our research, we found 3 types of resource-based policies with default versions set to 2008-10-17:

The below table shows the AWS services we tested based on AWS Services that work with IAM (opens in a new tab). From our research, we found some services default to 2012-10-17, some services default to 2008-10-17, some services default to empty policies (Empty), and some services did not support writing full IAM policy JSON.

AWS ServiceDefault Policy Version ElementComments
AWS SQS2008Both Console and API/CLI
AWS SNS2008Both Console and API/CLI
AWS VPC Endpoints2008Gateway Endpoints Only
AWS VPC Endpoints2012Interface Endpoints (Did not test all Interface Endpoints)
AWS Lambda2012Console
AWS SES2012Console
AWS GlueEmptyConsole
AWS EFSEmptyConsole
AWS KMS2012Console
AWS Secrets Manager2012Console
AWS ECR (Repository)2012Console
AWS CloudWatch Logs2012API/CLI Only
AWS S32012Console
AWS IAM2012Console
AWS Private CAUnknownAPI/CLI Only
AWS API Gateway2012Console
AWS Cloud9NADoes not support full IAM Policy Language
AWS SAMNADoes not support full IAM Policy Language
AWS Backup2012Console
AWS S3 GlacierEmptyConsole
AWS S3 OutpostsUnknownDid Not Test
AWS CodeArtifact2012Console
AWS Lex v2EmptyConsole
AWS OpenSearch2012Console
AWS EventBridge Schemas2012Console
AWS EventBridge Event Buses2012Console
AWS Elemental MediaStore2012Console
AWS CloudTrail2012Console

Finding Resource-Based Policies with CloudQuery

We will use CloudQuery to check for resource-based policies with 2008-10-17 explicitly set as the version or for resource-based policies without an explicit version set. The following queries will be used to check for SQS queue policies, SNS topic policies, and VPC Endpoint policies. Additional queries can be written for other AWS resource-based policies.

Finding SQS Queues and Their Resource-Based Policies

SELECT * FROM 
    (
        SELECT policy ->> 'Version' as version, *
        FROM aws_sqs_queues 
    ) as sqs_queues
WHERE version = '2008-10-17'
OR version is NULL; 

Finding SNS Topics and Their Resource-Based Policies

SELECT * FROM 
    (
        SELECT policy ->> 'Version' as version, *
        FROM aws_sns_topics
    ) as sns_topics
WHERE version = '2008-10-17'
OR version is NULL; 

Finding VPC Endpoints and Their Resource-Based Policies

SELECT * FROM 
    (
        SELECT policy_document ->> 'Version' as version, *
        FROM aws_vpc_endpoints
    ) as vpc_endpoints
WHERE version = '2008-10-17'
OR version is NULL; 

Conclusion

The legacy policy version 2008-10-17 can be problematic for developers and application teams given the difference in interpretation by AWS.

We would like to see the following from AWS:

  • Example and default policies should have default policy versions set to 2012-10-17.
  • Not specifying a policy version should also default to 2012-10-17 once enough warning has been given to AWS customers.
  • In the interim, more controls similar to the IAM Managed Policy creation experience that block creation of new policies without 2012-10-17 specified.

In the interim, we have the following recommendations for AWS end users:

  • Scan AWS environments for any legacy usage of the 2008-10-17 policy language version.
  • If possible, update legacy policies to use the 2012-10-17 policy language version.
  • Ensure all new policies leverage the newer 2012-10-17 policy language version.

Stay tuned for more use cases we can build with CloudQuery on top of the infrastructure data we loaded from AWS!

Contact Us

If you have comments or questions about IAM, CloudQuery, or potential partnerships with us, we would love to hear from you! Reach out to us on GitHub (opens in a new tab) or Discord (opens in a new tab)!

References and Useful Links

CloudQuery: AWS Plugin (opens in a new tab)

AWS: IAM JSON policy elements: Version (opens in a new tab)

AWS: Grammar of the IAM JSON Policy Language (opens in a new tab)

AWS: AWS Services that work with IAM (opens in a new tab)

AWS: IAM Policy elements: Variables and Tags (opens in a new tab)