PS Product SecurityKnowledge Base

API Gateway Policy Examples

API Gateway Policy Patterns

Intro: API gateways are not the whole security model, but they are an important outer control layer. The right gateway policy can reduce exposure before a request even reaches service-level authentication and authorization logic.

What this page includes

  • policy examples for AWS API Gateway resource policies
  • patterns for public, private, and cross-account access control
  • how gateway policy complements app-layer auth rather than replacing it

Working assumptions

  • gateway policy should narrow ingress aggressively, then let the application enforce business authorization

What gateway policy is good at

Gateway policy is strong at constraining:

  • who can call an API;
  • from where the call may originate;
  • which stages, methods, or paths are exposed;
  • which environments should be reachable from the public internet or only from private entry points.

Gateway policy is not where business authorization logic belongs. Object ownership, tenancy checks, entitlement checks, and workflow-level controls still belong in the application.

Example 1: allow specific cross-account roles

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::111122223333:role/developer",
          "arn:aws:iam::111122223333:role/Admin"
        ]
      },
      "Action": "execute-api:Invoke",
      "Resource": [
        "execute-api:/prod/GET/orders",
        "execute-api:/prod/POST/orders"
      ]
    }
  ]
}

Use this when a partner account or central automation account needs narrowly scoped invoke rights.

Example 2: deny calls outside trusted source IP ranges

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Principal": "*",
      "Action": "execute-api:Invoke",
      "Resource": "execute-api:/*",
      "Condition": {
        "NotIpAddress": {
          "aws:SourceIp": [
            "203.0.113.0/24",
            "198.51.100.10/32"
          ]
        }
      }
    },
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "execute-api:Invoke",
      "Resource": "execute-api:/*"
    }
  ]
}

Use this when you have a known corporate or partner egress range and want the gateway to reject everything else before the request reaches the service.

Example 3: allow private API access only from a VPC endpoint

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Principal": "*",
      "Action": "execute-api:Invoke",
      "Resource": "execute-api:/*",
      "Condition": {
        "StringNotEquals": {
          "aws:SourceVpce": "vpce-0abc1234def567890"
        }
      }
    },
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "execute-api:Invoke",
      "Resource": "execute-api:/*"
    }
  ]
}

Use this for private APIs that should only be reachable through an approved VPC endpoint.

Example 4: allow private API access only from a specific VPC

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Principal": "*",
      "Action": "execute-api:Invoke",
      "Resource": "execute-api:/*",
      "Condition": {
        "StringNotEquals": {
          "aws:SourceVpc": "vpc-0123456789abcdef0"
        }
      }
    },
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "execute-api:Invoke",
      "Resource": "execute-api:/*"
    }
  ]
}

Use when the policy boundary is the source VPC rather than a specific endpoint.

Example 5: method and stage scoping

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111122223333:role/reporting-client"
      },
      "Action": "execute-api:Invoke",
      "Resource": [
        "execute-api:/staging/GET/reports/*"
      ]
    }
  ]
}

Do not grant execute-api:/* unless the role truly needs every stage, path, and method.

Terraform example for resource policy attachment

resource "aws_api_gateway_rest_api_policy" "orders_api" {
  rest_api_id = aws_api_gateway_rest_api.orders.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect    = "Deny"
        Principal = "*"
        Action    = "execute-api:Invoke"
        Resource  = "execute-api:/*"
        Condition = {
          StringNotEquals = {
            "aws:SourceVpce" = aws_vpc_endpoint.execute_api.id
          }
        }
      },
      {
        Effect    = "Allow"
        Principal = "*"
        Action    = "execute-api:Invoke"
        Resource  = "execute-api:/*"
      }
    ]
  })
}

How gateway policy complements application auth

Layer Good at Not enough for
gateway policy network and principal boundary object ownership, role entitlements, tenant checks
JWT / IAM auth verifying caller identity business-specific authorization decisions
application authorization data access, tenancy, workflow rules early traffic narrowing

Common mistakes

Mistake Consequence
using the gateway as the only authorization layer business logic bypass risk remains
granting broad execute-api:/* permissions overexposure of methods and stages
leaving private APIs reachable from the wrong VPC/VPCE path internal boundary collapses
mixing prod and non-prod policy environment separation becomes fragile

Author attribution: Ivan Piskunov, 2026 - Educational and defensive-engineering use.