Skip to content

aws-apigateway: Allowed origins for CORS preflight are treated as regular expressions #26623

@cyraxx

Description

@cyraxx

Describe the bug

The CDK constructs for API Gateway allow supplying multiple trusted origins to be used in the auto-generated OPTIONS method handlers for CORS preflight responses (CorsOptions interface).

The CORS specification only allows for a single origin in the response headers, so what CDK does in this case is generate a response template that modifies the response based on the origin that the client is requesting. It checks each of the supplied origins (beyond the first one) to see if it matches the Origin request header, and if it does, it sets the Access-Control-Allow-Origin response header to the origin that was supplied in the request.

However, this check treats the configured origin as a regular expression (the code in question). Therefore, if https://www.company.com has been configured as an allowed origin, an attacker could register the domain https://wwwxcompany.com. A crafted request from this origin will match the regular expression and the preflight request succeeds.

It's possible that this is intended behavior, however the documentation doesn't mention anything to that effect. It would also be inconsistent as it does not apply to the first specified origin, only additional ones.

Expected Behavior

Given an API Gateway resource configured with the CORS preflight origins "https://www.firstorigin.com", "https://www.secondorigin.com":

An OPTIONS request to the resource with a request header of Origin: https://wwwXsecondorigin.com should lead to a response header similar to Access-Control-Allow-Origin: https://www.firstorigin.com, i.e. not allowing the invalid origin from the request.

Current Behavior

The OPTIONS request leads to a response header of Access-Control-Allow-Origin: https://wwwXsecondorigin.com, signaling to the requesting browser that the requested origin from is valid.

Reproduction Steps

Create a POC stack such as:

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';

export class CorsPocStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    new apigateway.RestApi(this, 'api', {
      defaultCorsPreflightOptions: {
        allowOrigins: ['https://www.firstorigin.com', 'https://www.secondorigin.com'],
      }
    })
      .root
      .addResource('hello')
      .addMethod('GET', new apigateway.MockIntegration({}));
  }
}

Deploy the stack.

Call the CORS preflight method, e.g. using httpie:
http OPTIONS https://xxx.execute-api.xxx.amazonaws.com/prod/hello "Origin: https://wwwXsecondorigin.com"

Examine the response and see that it includes the header: Access-Control-Allow-Origin: https://wwwXsecondorigin.com

Possible Solution

Change $origin.matches("${o}") to $origin == "${o}"

Additional Information/Context

No response

CDK CLI Version

2.89.0 (build 2ad6683)

Framework Version

No response

Node.js Version

v20.2.0

OS

macOS

Language

Typescript

Language Version

TypeScript (5.1.6)

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    @aws-cdk/aws-apigatewayRelated to Amazon API GatewaybugThis issue is a bug.effort/smallSmall work item – less than a day of effortp1

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions