HCSEC-2023-26 - Terraform’s Handling Of Duplicate Map Keys In Configurations May Have Security Implications

Bulletin ID: HCSEC-2023-26
Affected Products / Versions: All Terraform releases to date.
Publication Date: August 24, 2023

A long-lived Terraform issue regarding silent replacement of map entries with duplicate keys was recently observed to have security implications in one specific scenario.

External security research highlighted this Terraform behavior specifically when configuring IAM roles used to control OIDC authentication flows via the AWS provider with inline security policies. When an inline security policy was specified with multiple conditions using duplicate map keys, all but the last condition were silently replaced.

While this behavior itself may not be considered an exploitable vulnerability, and the terraform plan output does reflect the actual end state correctly, this behavior may be unexpected. It may also extend to other scenarios.

The Terraform configuration language uses values of different types to track expression results, one type of which is the map, a group of values identified by named labels (eg. {name = "Mabel", age = 52}).

During standard Terraform operation, configuration values may be serialized into JSON representations both explicitly within the configuration (see Terraform’s jsonencode function) and implicitly (used by some providers for data normalization). This serialization depends on Go’s encoding/json package.

One use of Terraform maps is to configure IAM policies with the AWS provider, using aws_iam_role, aws_iam_policy, and aws_iam_role_policy resources.

External security researchers recently reported and subsequently highlighted what might be unexpected behavior by the Terraform AWS provider when processing configurations for inline security policies with duplicate map keys.

When processing maps with duplicate keys, Terraform silently replaces all but the last entry. This behavior during JSON serialization is similar to some other languages (eg. Go [which Terraform inherits this behavior from], Python, and JavaScript) and tools (eg. jq).

While the terraform plan output reflects the actual end state correctly, it does not warn about duplicate keys and the difference between configuration and plan may not be noticed by a practitioner who assumes a clean Terraform run means their policy configuration is applied as written.

Policy configurations that are affected by this behavior may be specified with heredoc strings or jsonencode.

For example, a problematic inline policy configuration (note the duplicate StringEquals keys within the Condition) might be as follows:

resource "aws_iam_role" "example_role" {
  name = "[snip]"
  assume_role_policy = jsonencode({
    Statement = [{
      "Effect" : "Allow",
      "Condition" : {
        "StringEquals" : { "[snip]": "[snip]"},
        "StringEquals" : { "[snip]": "[snip]"}

When terraform plan is run, the output and end state is a policy that only includes the second StringEquals entry.

Another problematic inline policy configuration would be similar to the above, but would configure the policy using a heredoc string without the jsonencode call.

An inline policy configuration that is not affected by this behavior may specify a Condition with two different tests (eg. StringEquals and StringLike), avoiding the use of duplicate keys.

Another policy configuration which is not affected may use the aws_iam_policy_document data source. Multiple condition blocks with the same test value and different variable values are aggregated into a policy condition that uses all configured conditions.

During investigation of this issue, we tested behavior of the AWS Management Console and CLI tool. In both cases, policies with duplicate Condition keys as in the example above were rejected with an error (Duplicate JSON key: StringEquals and Duplicate condition element: StringEquals respectively.)

Due to potential impact on Terraform core and HCL dependencies, this behavior is not something that can be immediately modified. While the security implications identified to date are restricted to a specific situation, the implications of a behavior change are potentially much wider.

In response to this situation, HashiCorp has:

  • Reviewed and discussed the original issue, specifically how this behavior occurs through a combined interaction of Terraform configuration, Terraform core, HCL constructs, and the Terraform AWS provider.
  • Initiated efforts to modify the AWS provider to specifically warn on the problematic IAM policy situation noted above and to update documentation.
  • Developed an initial set of semgrep rules for detecting potentially problematic policy definitions within a Terraform configuration.
  • Identified and addressed a similar consistency issue with the AWS provider’s aws_iam_policy_document data source.

Terraform practitioners should be cautious of configurations that may be affected by this behavior and take appropriate action.

This may include:

  • Review and follow documented guidance regarding creation of IAM policies. Specifically, consider standardizing on usage of the aws_iam_policy_document data source when configuring AWS IAM policies.
  • Review existing AWS IAM policy configurations or adopt tooling to ensure that policies are configured and applied in line with security requirements.
  • Upgrade to a new release of the Terraform AWS provider, when available.

More generally, this does highlight the need for Terraform practitioners to review terraform plan output, validating configuration changes and ensuring that the end state is as expected.

Original security research was published by Datadog Security Labs, with security implications further highlighted by Wiz.

We deeply appreciate any effort to coordinate disclosure of security vulnerabilities. For information about security at HashiCorp and the reporting of security vulnerabilities, please see https://hashicorp.com/security.