Zum Inhalt

CORS (Cross-Origin Resource Sharing) Anleitung

Umfassende Anleitung für CORS-Policies in GAL (Gateway Abstraction Layer)

Inhaltsverzeichnis

  1. Übersicht
  2. Schnellstart
  3. Konfigurationsoptionen
  4. Provider-Implementierung
  5. Häufige Anwendungsfälle
  6. Sicherheits-Best-Practices
  7. CORS Testen
  8. Troubleshooting

Übersicht

CORS (Cross-Origin Resource Sharing) ist ein Sicherheitsmechanismus, der es Webanwendungen ermöglicht, Ressourcen von verschiedenen Ursprüngen (Origins) anzufordern. GAL bietet eine einheitliche CORS-Konfiguration für alle unterstützten Gateway-Provider.

Was ist CORS?

CORS ermöglicht es Browsern, Cross-Origin-Requests durchzuführen, die normalerweise durch die Same-Origin-Policy blockiert würden. Beispiel:

  • Frontend: https://app.example.com
  • API: https://api.example.com

Ohne CORS würde der Browser die Requests vom Frontend zur API blockieren, da sie unterschiedliche Origins haben.

Warum ist CORS wichtig?

  • Sicherheit: Kontrolliert, welche Domains auf deine API zugreifen dürfen
  • Flexibilität: Ermöglicht moderne Frontend-Architekturen (SPA, PWA)
  • Browser-Kompatibilität: Funktioniert mit allen modernen Browsern
  • Credentials-Support: Ermöglicht Cookies und Authentication headers

Unterstützte Features

Feature Kong APISIX Traefik Envoy Beschreibung
Allowed Origins Welche Origins dürfen zugreifen
Allowed Methods Erlaubte HTTP-Methoden
Allowed Headers Erlaubte Request-Headers
Exposed Headers Headers die an Client zurückgegeben werden
Credentials Cookies/Auth headers erlauben
Max Age Preflight-Cache-Dauer

Schnellstart

Einfache CORS-Konfiguration

Erlaube alle Origins (nur für Entwicklung!):

version: "1.0"
provider: kong

services:
  - name: api_service
    type: rest
    protocol: http
    upstream:
      host: api.example.com
      port: 8080
    routes:
      - path_prefix: /api
        methods: [GET, POST, OPTIONS]
        cors:
          enabled: true
          allowed_origins: ["*"]
          allowed_methods: [GET, POST, PUT, DELETE, OPTIONS]
          allowed_headers: [Content-Type, Authorization]

Produktions-CORS-Konfiguration

Beschränke auf spezifische Origins:

routes:
  - path_prefix: /api
    methods: [GET, POST, OPTIONS]
    cors:
      enabled: true
      allowed_origins:
        - "https://app.example.com"
        - "https://www.example.com"
      allowed_methods: [GET, POST, PUT, DELETE, OPTIONS]
      allowed_headers: [Content-Type, Authorization, X-API-Key]
      expose_headers: [X-Request-ID, X-RateLimit-Remaining]
      allow_credentials: true
      max_age: 86400  # 24 Stunden

CORS mit Authentication

Kombiniere CORS mit JWT-Authentication:

routes:
  - path_prefix: /api/protected
    methods: [GET, POST, OPTIONS]

    # CORS-Konfiguration
    cors:
      enabled: true
      allowed_origins: ["https://app.example.com"]
      allowed_methods: [GET, POST, OPTIONS]
      allowed_headers: [Content-Type, Authorization]
      allow_credentials: true

    # Authentication
    authentication:
      enabled: true
      type: jwt
      jwt:
        issuer: "https://auth.example.com"
        audience: "api.example.com"
        jwks_uri: "https://auth.example.com/.well-known/jwks.json"

Konfigurationsoptionen

CORSPolicy Felder

cors:
  # Aktivierung
  enabled: true

  # Erlaubte Origins (Domains)
  allowed_origins:
    - "https://app.example.com"
    - "https://admin.example.com"
    # Oder Wildcard (NUR für Entwicklung!)
    # - "*"

  # Erlaubte HTTP-Methoden
  allowed_methods:
    - GET
    - POST
    - PUT
    - DELETE
    - OPTIONS  # Wichtig für Preflight!

  # Erlaubte Request-Headers
  allowed_headers:
    - Content-Type
    - Authorization
    - X-API-Key
    - X-Custom-Header

  # Headers die an Browser zurückgegeben werden
  expose_headers:
    - X-Request-ID
    - X-Response-Time
    - X-RateLimit-Remaining

  # Credentials erlauben (Cookies, Auth headers)
  allow_credentials: true

  # Preflight-Cache-Dauer (in Sekunden)
  max_age: 86400  # 24 Stunden

Standard-Werte

Wenn Felder weggelassen werden, gelten folgende Defaults:

cors:
  enabled: true
  allowed_origins: ["*"]
  allowed_methods: [GET, POST, PUT, DELETE, OPTIONS]
  allowed_headers: [Content-Type, Authorization]
  expose_headers: []
  allow_credentials: false
  max_age: 86400

Wildcard Origins (*)

⚠️ Warnung: Wildcard Origins sind unsicher für Produktion!

# ❌ NICHT in Produktion verwenden!
cors:
  allowed_origins: ["*"]
  allow_credentials: true  # Funktioniert nicht mit "*"!

# ✅ Stattdessen spezifische Origins:
cors:
  allowed_origins:
    - "https://app.example.com"
    - "https://admin.example.com"
  allow_credentials: true

Wichtig: Wenn allow_credentials: true, dann darf NICHT * als Origin verwendet werden!


Provider-Implementierung

Kong (cors plugin)

Kong verwendet das native cors Plugin:

# GAL Konfiguration
cors:
  enabled: true
  allowed_origins: ["https://app.example.com"]
  allowed_methods: [GET, POST]
  allow_credentials: true

# Wird zu Kong-Config:
plugins:
  - name: cors
    config:
      origins:
        - "https://app.example.com"
      methods:
        - GET
        - POST
      headers:
        - Content-Type
        - Authorization
      credentials: true
      max_age: 86400

Kong Besonderheiten: - ✅ Vollständige CORS-Unterstützung - ✅ Native Plugin-Integration - ✅ Regex-Support für Origins - ⚠️ OPTIONS-Methode muss in Route-Methoden enthalten sein

APISIX (cors plugin)

APISIX verwendet das cors Plugin mit komma-separiertem Format:

# GAL Konfiguration
cors:
  enabled: true
  allowed_origins: ["https://app.example.com", "https://www.example.com"]
  allowed_methods: [GET, POST]

# Wird zu APISIX-Config:
{
  "plugins": {
    "cors": {
      "allow_origins": "https://app.example.com,https://www.example.com",
      "allow_methods": "GET,POST",
      "allow_headers": "Content-Type,Authorization",
      "allow_credential": true,
      "max_age": 86400
    }
  }
}

APISIX Besonderheiten: - ✅ Native CORS-Plugin - ⚠️ Komma-separierte Listen (automatisch konvertiert) - ⚠️ allow_credential (singular, nicht plural!)

Traefik (headers middleware)

Traefik implementiert CORS via Headers-Middleware:

# GAL Konfiguration
cors:
  enabled: true
  allowed_origins: ["https://app.example.com"]
  allowed_methods: [GET, POST]

# Wird zu Traefik-Config:
middlewares:
  api_router_0_cors:
    headers:
      accessControlAllowMethods:
        - GET
        - POST
      accessControlAllowOriginList:
        - "https://app.example.com"
      accessControlAllowHeaders:
        - Content-Type
        - Authorization
      accessControlAllowCredentials: true
      accessControlMaxAge: 86400

Traefik Besonderheiten: - ✅ Headers-Middleware für CORS - ✅ accessControl* Konfiguration - ⚠️ Middleware muss in Router referenziert werden

Envoy (native CORS policy)

Envoy hat native CORS-Unterstützung auf Route-Level:

# GAL Konfiguration
cors:
  enabled: true
  allowed_origins: ["https://app.example.com"]
  allowed_methods: [GET, POST]

# Wird zu Envoy-Config:
routes:
  - match:
      prefix: /api
    route:
      cluster: api_cluster
    cors:
      allow_origin_string_match:
        - exact: "https://app.example.com"
      allow_methods: "GET, POST"
      allow_headers: "Content-Type, Authorization"
      allow_credentials: true
      max_age: "86400"

Envoy Besonderheiten: - ✅ Native CORS-Policy - ✅ Regex-Support für Origins - ⚠️ Wildcard * wird zu safe_regex: '.*' konvertiert


Nginx (add_header directive)

Nginx implementiert CORS mit add_header Directives in Location-Blöcken:

# GAL generiert Nginx-Config
location /api {
    # Handle OPTIONS (Preflight) Request
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' 'https://app.example.com' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-API-Key' always;
        add_header 'Access-Control-Max-Age' '86400' always;
        add_header 'Content-Type' 'text/plain charset=UTF-8';
        add_header 'Content-Length' '0';
        return 204;
    }

    # Handle Actual Request
    add_header 'Access-Control-Allow-Origin' 'https://app.example.com' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-API-Key' always;
    add_header 'Access-Control-Expose-Headers' 'X-Request-ID, X-RateLimit-Remaining' always;

    proxy_pass http://backend;
}

Mit OpenResty (Lua):

location /api {
    access_by_lua_block {
        local origin = ngx.var.http_origin
        local allowed_origins = {
            ["https://app.example.com"] = true,
            ["https://admin.example.com"] = true
        }

        if allowed_origins[origin] then
            ngx.header["Access-Control-Allow-Origin"] = origin
            ngx.header["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
            ngx.header["Access-Control-Allow-Headers"] = "Content-Type, Authorization"
            ngx.header["Access-Control-Allow-Credentials"] = "true"
            ngx.header["Access-Control-Max-Age"] = "86400"

            if ngx.var.request_method == "OPTIONS" then
                ngx.exit(204)
            end
        end
    }

    proxy_pass http://backend;
}

Nginx Besonderheiten: - ✅ Native add_header Support für CORS Headers - ✅ if ($request_method = 'OPTIONS') für Preflight Handling - ✅ always Flag wichtig (Header auch bei Errors) - ✅ OpenResty/Lua für dynamische Origin-Validierung - ⚠️ Viele Origins = Viele if-Blocks (nicht optimal) - ⚠️ Lua-basierte Lösung empfohlen für Production

Testing:

# Preflight Request
curl -X OPTIONS \
  -H "Origin: https://app.example.com" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: Content-Type" \
  -v \
  http://nginx-host/api

# Response:
# HTTP/1.1 204 No Content
# Access-Control-Allow-Origin: https://app.example.com
# Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
# Access-Control-Allow-Headers: Content-Type, Authorization, X-API-Key
# Access-Control-Max-Age: 86400


HAProxy (http-response set-header)

HAProxy implementiert CORS mit http-response set-header Directives:

# GAL generiert HAProxy-Config
frontend api_front
    bind *:80

    # CORS Headers für alle Responses
    http-response set-header Access-Control-Allow-Origin "https://app.example.com"
    http-response set-header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
    http-response set-header Access-Control-Allow-Headers "Content-Type, Authorization, X-API-Key"
    http-response set-header Access-Control-Expose-Headers "X-Request-ID, X-RateLimit-Remaining"
    http-response set-header Access-Control-Max-Age "86400"

    # Handle OPTIONS (Preflight) Request
    acl is_options method OPTIONS
    http-request deny deny_status 204 if is_options

    default_backend api_backend

backend api_backend
    server backend1 backend.example.com:8080

Mit Multiple Origins (ACL-basiert):

frontend api_front
    bind *:80

    # Define allowed origins
    acl origin_app hdr(origin) -i https://app.example.com
    acl origin_admin hdr(origin) -i https://admin.example.com
    acl valid_origin or origin_app origin_admin

    # Set CORS headers only for valid origins
    http-response set-header Access-Control-Allow-Origin %[req.hdr(Origin)] if valid_origin
    http-response set-header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" if valid_origin
    http-response set-header Access-Control-Allow-Headers "Content-Type, Authorization" if valid_origin
    http-response set-header Access-Control-Allow-Credentials "true" if valid_origin
    http-response set-header Access-Control-Max-Age "86400" if valid_origin

    # Handle OPTIONS preflight
    acl is_options method OPTIONS
    http-request return status 204 if is_options valid_origin

    default_backend api_backend

HAProxy Besonderheiten: - ✅ http-response set-header für CORS Headers - ✅ ACL-basierte Origin-Validierung (sehr performant) - ✅ http-request return status 204 für Preflight (HAProxy 2.2+) - ✅ %[req.hdr(Origin)] für dynamische Origin-Reflection - ⚠️ Ältere HAProxy Versionen (<2.2): http-request deny deny_status 204 statt return - ✅ ACL-Validierung verhindert offene CORS-Policies

Testing:

# Preflight Request
curl -X OPTIONS \
  -H "Origin: https://app.example.com" \
  -H "Access-Control-Request-Method: POST" \
  -v \
  http://haproxy-host/api

# Response:
# HTTP/1.1 204 No Content
# Access-Control-Allow-Origin: https://app.example.com
# Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
# Access-Control-Allow-Headers: Content-Type, Authorization
# Access-Control-Max-Age: 86400

# Actual Request
curl -X GET \
  -H "Origin: https://app.example.com" \
  http://haproxy-host/api

# Response Headers include:
# Access-Control-Allow-Origin: https://app.example.com

HAProxy CORS Best Practices: 1. Verwende ACLs für Origin-Validierung (nicht * Wildcard) 2. Nutze %[req.hdr(Origin)] für dynamische Origin-Reflection 3. HAProxy 2.2+: Verwende http-request return status 204 für Preflight 4. Setze Access-Control-Max-Age hoch (86400 = 24h) für Performance 5. Teste immer Preflight (OPTIONS) und Actual Request separat


GCP API Gateway (OPTIONS methods)

GCP API Gateway implementiert CORS mit expliziten OPTIONS Methods in OpenAPI 2.0:

# GAL Konfiguration
cors:
  enabled: true
  allowed_origins: ["https://app.example.com", "*"]
  allowed_methods: [GET, POST, PUT]
  allowed_headers: [Content-Type, Authorization]
  allow_credentials: false
  max_age: 3600

# Wird zu OpenAPI 2.0 Config:
swagger: "2.0"
paths:
  /api/users:
    # Actual endpoint
    get:
      summary: "Get users"
      responses:
        200:
          description: "Success"

    # CORS Preflight (OPTIONS)
    options:
      summary: "CORS preflight"
      operationId: "corsUsers"
      responses:
        200:
          description: "CORS headers"
          headers:
            Access-Control-Allow-Origin:
              type: "string"
              description: "https://app.example.com"
            Access-Control-Allow-Methods:
              type: "string"
              description: "GET, POST, PUT, OPTIONS"
            Access-Control-Allow-Headers:
              type: "string"
              description: "Content-Type, Authorization"
            Access-Control-Max-Age:
              type: "string"
              description: "3600"

Deployment:

# OpenAPI Spec mit CORS OPTIONS methods deployen
gcloud api-gateway api-configs create cors-api-config \
  --api=my-api \
  --openapi-spec=openapi-cors.yaml \
  --project=my-gcp-project

# Gateway aktualisieren
gcloud api-gateway gateways update my-gateway \
  --api=my-api \
  --api-config=cors-api-config \
  --location=us-central1 \
  --project=my-gcp-project

CORS Testen:

# Preflight Request (OPTIONS)
curl -X OPTIONS \
  -H "Origin: https://app.example.com" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: Content-Type, Authorization" \
  https://my-gateway-xyz.apigateway.my-gcp-project.cloud.goog/api/users

# Erwartete Response Headers:
# Access-Control-Allow-Origin: https://app.example.com
# Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS
# Access-Control-Allow-Headers: Content-Type, Authorization
# Access-Control-Max-Age: 3600

# Actual Request mit CORS
curl -X GET \
  -H "Origin: https://app.example.com" \
  -H "Authorization: Bearer <JWT>" \
  https://my-gateway-xyz.apigateway.my-gcp-project.cloud.goog/api/users

# Response Headers enthalten:
# Access-Control-Allow-Origin: https://app.example.com

GCP API Gateway Besonderheiten: - ✅ CORS wird durch explizite OPTIONS Methods implementiert - ✅ Access-Control-* Headers werden automatisch in Responses hinzugefügt - ✅ Wildcard * für allowed_origins wird unterstützt - ⚠️ OPTIONS Methods müssen für jeden Pfad explizit definiert werden - ⚠️ CORS-Konfiguration erfolgt auf OpenAPI-Ebene, nicht auf Gateway-Ebene - ✅ Preflight-Caching mit Access-Control-Max-Age wird unterstützt - ✅ Credentials (Access-Control-Allow-Credentials) werden unterstützt

Wichtige Hinweise: - GCP API Gateway hat keine native CORS-Konfiguration wie Kong/APISIX - CORS wird durch OpenAPI 2.0 OPTIONS Methods implementiert - GAL generiert automatisch OPTIONS Methods für alle Routen mit cors.enabled: true - Für Production: Verwende spezifische Origins statt Wildcard * mit Credentials


AWS API Gateway (OPTIONS methods mit Mock Integration)

AWS API Gateway implementiert CORS mit expliziten OPTIONS Methods und Mock Integrations in OpenAPI 3.0:

AWS API Gateway CORS-Mechanismus: - Mechanismus: OpenAPI 3.0 OPTIONS Methods mit x-amazon-apigateway-integration (type: mock) - Features: Automatic OPTIONS Method Generation, Gateway Responses (DEFAULT_4XX, DEFAULT_5XX), Response Headers Mapping - Hinweis: CORS wird durch explizite OPTIONS Methods implementiert, keine native Gateway-Level CORS-Config

Generiertes Config-Beispiel:

{
  "openapi": "3.0.1",
  "info": {
    "title": "CORS API",
    "version": "1.0.0"
  },
  "paths": {
    "/api/users": {
      "get": {
        "summary": "Get users",
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "Access-Control-Allow-Origin": {
                "schema": {"type": "string"}
              }
            }
          }
        },
        "x-amazon-apigateway-integration": {
          "type": "http_proxy",
          "httpMethod": "GET",
          "uri": "https://backend.example.com/api/users",
          "responses": {
            "default": {
              "statusCode": "200",
              "responseParameters": {
                "method.response.header.Access-Control-Allow-Origin": "'https://app.example.com'"
              }
            }
          }
        }
      },
      "options": {
        "summary": "CORS preflight",
        "responses": {
          "200": {
            "description": "CORS preflight response",
            "headers": {
              "Access-Control-Allow-Origin": {"schema": {"type": "string"}},
              "Access-Control-Allow-Methods": {"schema": {"type": "string"}},
              "Access-Control-Allow-Headers": {"schema": {"type": "string"}},
              "Access-Control-Max-Age": {"schema": {"type": "string"}}
            }
          }
        },
        "x-amazon-apigateway-integration": {
          "type": "mock",
          "requestTemplates": {
            "application/json": "{\"statusCode\": 200}"
          },
          "responses": {
            "default": {
              "statusCode": "200",
              "responseParameters": {
                "method.response.header.Access-Control-Allow-Origin": "'https://app.example.com'",
                "method.response.header.Access-Control-Allow-Methods": "'GET,POST,PUT,DELETE,OPTIONS'",
                "method.response.header.Access-Control-Allow-Headers": "'Content-Type,Authorization,X-Api-Key'",
                "method.response.header.Access-Control-Max-Age": "'86400'"
              }
            }
          }
        }
      }
    }
  }
}

Deployment:

# OpenAPI mit CORS generieren
gal generate -c config.yaml -p aws_apigateway -o api.json

# API erstellen/updaten
API_ID=$(aws apigateway import-rest-api \
  --body file://api.json \
  --query 'id' --output text)

# Gateway Responses für 4XX/5XX CORS Headers erstellen
aws apigateway put-gateway-response \
  --rest-api-id $API_ID \
  --response-type DEFAULT_4XX \
  --response-parameters \
    "gatewayresponse.header.Access-Control-Allow-Origin='https://app.example.com'" \
    "gatewayresponse.header.Access-Control-Allow-Methods='GET,POST,PUT,DELETE,OPTIONS'" \
    "gatewayresponse.header.Access-Control-Allow-Headers='Content-Type,Authorization'"

aws apigateway put-gateway-response \
  --rest-api-id $API_ID \
  --response-type DEFAULT_5XX \
  --response-parameters \
    "gatewayresponse.header.Access-Control-Allow-Origin='https://app.example.com'"

# Deployment erstellen
aws apigateway create-deployment \
  --rest-api-id $API_ID \
  --stage-name prod

# API URL anzeigen
echo "https://${API_ID}.execute-api.us-east-1.amazonaws.com/prod"

CORS Testen:

# Preflight Request (OPTIONS)
curl -X OPTIONS \
  -H "Origin: https://app.example.com" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: Content-Type, Authorization" \
  -v \
  https://${API_ID}.execute-api.us-east-1.amazonaws.com/prod/api/users

# Erwartete Response Headers:
# HTTP/1.1 200 OK
# Access-Control-Allow-Origin: https://app.example.com
# Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
# Access-Control-Allow-Headers: Content-Type,Authorization,X-Api-Key
# Access-Control-Max-Age: 86400

# Actual Request mit CORS
curl -X GET \
  -H "Origin: https://app.example.com" \
  -H "Authorization: Bearer <token>" \
  https://${API_ID}.execute-api.us-east-1.amazonaws.com/prod/api/users

# Response Headers enthalten:
# Access-Control-Allow-Origin: https://app.example.com

Browser Preflight Ablauf:

1. Browser sendet OPTIONS Request:
   OPTIONS /api/users HTTP/1.1
   Origin: https://app.example.com
   Access-Control-Request-Method: POST
   Access-Control-Request-Headers: Content-Type, Authorization

2. API Gateway Mock Integration antwortet:
   HTTP/1.1 200 OK
   Access-Control-Allow-Origin: https://app.example.com
   Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
   Access-Control-Allow-Headers: Content-Type,Authorization,X-Api-Key
   Access-Control-Max-Age: 86400

3. Browser cached Preflight für 86400 Sekunden (24 Stunden)

4. Browser sendet Actual Request:
   POST /api/users HTTP/1.1
   Origin: https://app.example.com
   Content-Type: application/json
   Authorization: Bearer <token>

5. API Gateway fügt CORS Headers zu Response hinzu:
   HTTP/1.1 200 OK
   Access-Control-Allow-Origin: https://app.example.com
   Content-Type: application/json

AWS API Gateway CORS-spezifische Features: - ✅ OPTIONS Methods mit Mock Integration (keine Backend-Calls für Preflight) - ✅ Gateway Responses für 4XX/5XX Error CORS Headers - ✅ Response Parameters Mapping für dynamische CORS Headers - ✅ Wildcard * für allowed_origins unterstützt - ✅ Credentials (Access-Control-Allow-Credentials: true) unterstützt - ✅ Preflight-Caching mit Access-Control-Max-Age - ✅ CloudWatch Logs für CORS Preflight Requests

AWS-spezifische CORS-Konfigurationen:

# GAL Config für AWS CORS
services:
  - name: api
    routes:
      - path_prefix: /api/users
        methods: [GET, POST, PUT, DELETE]
        cors:
          enabled: true
          allowed_origins:
            - "https://app.example.com"
            - "https://admin.example.com"
          allowed_methods: [GET, POST, PUT, DELETE, OPTIONS]
          allowed_headers: [Content-Type, Authorization, X-Api-Key]
          expose_headers: [X-Request-ID, X-RateLimit-Remaining]
          allow_credentials: true
          max_age: 86400  # 24 Stunden

# GAL generiert automatisch:
# - OPTIONS Method für /api/users mit Mock Integration
# - Response Parameters für alle CORS Headers
# - Gateway Responses für 4XX/5XX CORS Headers

Troubleshooting:

Problem 1: CORS Headers fehlen bei 4XX/5XX Errors

# Lösung: Gateway Responses konfigurieren
aws apigateway put-gateway-response \
  --rest-api-id $API_ID \
  --response-type DEFAULT_4XX \
  --response-parameters \
    "gatewayresponse.header.Access-Control-Allow-Origin='*'"

Problem 2: Preflight Request schlägt fehl (OPTIONS 403)

# Prüfe: OPTIONS Method ist in OpenAPI definiert
# Prüfe: OPTIONS Method hat Mock Integration (type: "mock")
# Prüfe: API Key Required ist false für OPTIONS (api_key_required: false)

Problem 3: Wildcard * funktioniert nicht mit Credentials

# Falsch:
cors:
  allowed_origins: ["*"]
  allow_credentials: true  # ❌ Nicht erlaubt!

# Richtig:
cors:
  allowed_origins: ["https://app.example.com"]  # ✅ Spezifische Origin
  allow_credentials: true

AWS API Gateway Besonderheiten: - ✅ CORS wird durch explizite OPTIONS Methods implementiert (wie GCP API Gateway) - ✅ Mock Integration für OPTIONS (keine Backend-Calls, sehr schnell) - ✅ Gateway Responses für Error CORS Headers (wichtig für Auth-Fehler!) - ⚠️ OPTIONS Methods müssen für jeden Pfad explizit definiert werden - ⚠️ CORS-Konfiguration erfolgt auf OpenAPI-Ebene, keine native Gateway-Config - ⚠️ Gateway Responses müssen separat für 4XX/5XX konfiguriert werden (nicht in OpenAPI) - ✅ Preflight-Caching reduziert OPTIONS Requests (max_age) - ✅ Wildcard * wird unterstützt (aber nicht mit Credentials!)

Best Practices: 1. Verwende Mock Integration für OPTIONS Methods (kein Backend-Call nötig) 2. Konfiguriere Gateway Responses für 4XX/5XX CORS Headers (wichtig für Auth-Fehler!) 3. Setze max_age auf 86400 (24 Stunden) für Preflight-Caching 4. Verwende spezifische Origins statt Wildcard * mit Credentials 5. Teste Preflight mit curl -X OPTIONS -v vor Production-Deployment 6. Prüfe CloudWatch Logs für CORS-Fehler (OPTIONS 403, 401)


Häufige Anwendungsfälle

1. Single-Page Application (SPA)

Frontend und Backend auf verschiedenen Domains:

# Frontend: https://app.example.com
# Backend: https://api.example.com

services:
  - name: api
    routes:
      - path_prefix: /api
        methods: [GET, POST, PUT, DELETE, OPTIONS]
        cors:
          enabled: true
          allowed_origins:
            - "https://app.example.com"
          allowed_methods: [GET, POST, PUT, DELETE, OPTIONS]
          allowed_headers: [Content-Type, Authorization]
          allow_credentials: true

2. Mehrere Frontend-Domains

Erlaube mehrere Subdomains:

cors:
  enabled: true
  allowed_origins:
    - "https://app.example.com"
    - "https://admin.example.com"
    - "https://mobile.example.com"
  allowed_methods: [GET, POST, PUT, DELETE, OPTIONS]
  allow_credentials: true

3. Public API (keine Credentials)

Öffentliche API ohne Authentication:

cors:
  enabled: true
  allowed_origins: ["*"]  # OK für public APIs
  allowed_methods: [GET, POST, OPTIONS]
  allowed_headers: [Content-Type]
  allow_credentials: false  # Wichtig!
  max_age: 86400

4. API mit Custom Headers

Erlaube spezielle API-Keys im Header:

cors:
  enabled: true
  allowed_origins: ["https://app.example.com"]
  allowed_methods: [GET, POST, OPTIONS]
  allowed_headers:
    - Content-Type
    - Authorization
    - X-API-Key        # Custom Header
    - X-Client-ID      # Custom Header
  expose_headers:
    - X-RateLimit-Limit
    - X-RateLimit-Remaining
    - X-RateLimit-Reset

5. Entwicklung vs. Produktion

Verschiedene CORS-Konfigurationen:

# development.yaml
cors:
  enabled: true
  allowed_origins: ["*"]
  allowed_methods: [GET, POST, PUT, DELETE, OPTIONS]
  allow_credentials: false

# production.yaml
cors:
  enabled: true
  allowed_origins:
    - "https://app.example.com"
    - "https://www.example.com"
  allowed_methods: [GET, POST, PUT, DELETE, OPTIONS]
  allowed_headers: [Content-Type, Authorization]
  allow_credentials: true

6. Mobile Apps

Erlaube spezielle Origins für mobile Apps:

cors:
  enabled: true
  allowed_origins:
    - "https://app.example.com"      # Web
    - "capacitor://localhost"        # Capacitor iOS
    - "http://localhost"             # Capacitor Android
  allowed_methods: [GET, POST, PUT, DELETE, OPTIONS]
  allow_credentials: true

7. GraphQL API

CORS für GraphQL-Endpoints:

services:
  - name: graphql_api
    routes:
      - path_prefix: /graphql
        methods: [GET, POST, OPTIONS]
        cors:
          enabled: true
          allowed_origins: ["https://app.example.com"]
          allowed_methods: [GET, POST, OPTIONS]
          allowed_headers:
            - Content-Type
            - Authorization
            - X-Apollo-Tracing  # GraphQL-spezifisch

8. WebSocket-Verbindungen

CORS für WebSocket-Upgrades:

services:
  - name: websocket_service
    routes:
      - path_prefix: /ws
        methods: [GET, OPTIONS]  # WebSocket uses GET for upgrade
        cors:
          enabled: true
          allowed_origins: ["https://app.example.com"]
          allowed_methods: [GET, OPTIONS]
          allowed_headers:
            - Sec-WebSocket-Protocol
            - Sec-WebSocket-Extensions

Sicherheits-Best-Practices

1. ❌ Niemals Wildcard mit Credentials

Falsch (Sicherheitslücke!):

cors:
  allowed_origins: ["*"]
  allow_credentials: true  # ❌ GEFÄHRLICH!

Richtig:

cors:
  allowed_origins:
    - "https://app.example.com"
  allow_credentials: true  # ✅ Sicher

2. ✅ Spezifische Origins verwenden

Falsch:

allowed_origins: ["*"]  # ❌ Zu permissiv

Richtig:

allowed_origins:
  - "https://app.example.com"
  - "https://admin.example.com"

3. ✅ Minimale Header-Rechte

Erlaube nur benötigte Headers:

# ❌ Zu permissiv
allowed_headers: ["*"]

# ✅ Nur benötigte Headers
allowed_headers:
  - Content-Type
  - Authorization
  - X-API-Key

4. ✅ OPTIONS-Methode nicht vergessen

Für Preflight-Requests:

routes:
  - path_prefix: /api
    methods: [GET, POST, PUT, DELETE, OPTIONS]  # ✅ OPTIONS!
    cors:
      enabled: true

5. ✅ HTTPS für Produktion

Verwende immer HTTPS-Origins in Produktion:

# ❌ HTTP in Produktion
allowed_origins:
  - "http://app.example.com"

# ✅ HTTPS in Produktion
allowed_origins:
  - "https://app.example.com"

6. ✅ Max Age begrenzen

Setze sinnvolle Preflight-Cache-Dauer:

# ❌ Zu lang (1 Jahr)
max_age: 31536000

# ✅ Sinnvoll (24 Stunden)
max_age: 86400

7. ✅ Expose nur benötigte Headers

Exponiere nur Headers die der Client wirklich braucht:

# ✅ Nur benötigte Headers
expose_headers:
  - X-Request-ID
  - X-RateLimit-Remaining

# ❌ Zu viele interne Headers
expose_headers:
  - X-Internal-Service
  - X-Database-Host  # Sensible Informationen!

CORS Testen

Mit cURL

Preflight-Request (OPTIONS) testen:

curl -X OPTIONS http://api.example.com/api/users \
  -H "Origin: https://app.example.com" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: Content-Type" \
  -v

Erwartete Response-Headers:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

Actual Request testen:

curl -X GET http://api.example.com/api/users \
  -H "Origin: https://app.example.com" \
  -v

Erwartete Response-Headers:

Access-Control-Allow-Origin: https://app.example.com

Mit JavaScript (Browser)

// Einfacher CORS-Test
fetch('https://api.example.com/api/users', {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json'
  },
  credentials: 'include'  // Wenn allow_credentials: true
})
.then(response => {
  console.log('CORS successful!');
  console.log('Response headers:', response.headers);
  return response.json();
})
.catch(error => {
  console.error('CORS error:', error);
});

Mit Python Requests

import requests

# Test Preflight
response = requests.options(
    'http://api.example.com/api/users',
    headers={
        'Origin': 'https://app.example.com',
        'Access-Control-Request-Method': 'POST',
        'Access-Control-Request-Headers': 'Content-Type'
    }
)

print('Preflight Response Headers:')
print(response.headers)

# Check CORS headers
assert 'Access-Control-Allow-Origin' in response.headers
assert response.headers['Access-Control-Allow-Origin'] == 'https://app.example.com'

Automatisierte Tests

def test_cors_headers():
    """Test CORS configuration"""
    response = requests.get(
        'http://api.example.com/api/users',
        headers={'Origin': 'https://app.example.com'}
    )

    # Check allowed origin
    assert 'Access-Control-Allow-Origin' in response.headers
    assert response.headers['Access-Control-Allow-Origin'] == 'https://app.example.com'

    # Check credentials
    if 'Access-Control-Allow-Credentials' in response.headers:
        assert response.headers['Access-Control-Allow-Credentials'] == 'true'

def test_cors_preflight():
    """Test CORS preflight request"""
    response = requests.options(
        'http://api.example.com/api/users',
        headers={
            'Origin': 'https://app.example.com',
            'Access-Control-Request-Method': 'POST'
        }
    )

    assert response.status_code == 200
    assert 'Access-Control-Allow-Methods' in response.headers

Troubleshooting

Problem 1: CORS-Fehler trotz Konfiguration

Symptom: Browser zeigt CORS-Fehler, obwohl CORS konfiguriert ist.

Lösung: 1. ✅ OPTIONS-Methode erlauben:

methods: [GET, POST, OPTIONS]  # OPTIONS nicht vergessen!

  1. ✅ Origin exakt matchen:

    # ❌ Falsch
    allowed_origins: ["https://app.example.com/"]
    
    # ✅ Richtig (ohne trailing slash)
    allowed_origins: ["https://app.example.com"]
    

  2. ✅ CORS vor Authentication:

    # Stelle sicher, dass CORS-Plugin VOR Authentication läuft
    

Problem 2: Credentials funktionieren nicht

Symptom: Cookies/Auth headers werden nicht gesendet.

Lösung:

# ❌ Wildcard mit Credentials
cors:
  allowed_origins: ["*"]
  allow_credentials: true

# ✅ Spezifische Origin
cors:
  allowed_origins: ["https://app.example.com"]
  allow_credentials: true

Client-Side:

// JavaScript: credentials einschließen
fetch('https://api.example.com/api', {
  credentials: 'include'  // Wichtig!
})

Problem 3: Preflight-Request schlägt fehl

Symptom: OPTIONS-Request gibt 401/403 zurück.

Lösung:

# Authentication für OPTIONS deaktivieren
routes:
  - path_prefix: /api
    methods: [GET, POST, OPTIONS]
    cors:
      enabled: true
    # CORS muss VOR Authentication kommen!

Problem 4: Custom Headers werden blockiert

Symptom: Custom Headers wie X-API-Key funktionieren nicht.

Lösung:

cors:
  allowed_headers:
    - Content-Type
    - Authorization
    - X-API-Key        # Custom Header explizit erlauben!

Client-Side:

fetch('https://api.example.com/api', {
  headers: {
    'X-API-Key': 'key123'  // Muss in allowed_headers sein!
  }
})

Problem 5: Expose-Headers nicht sichtbar

Symptom: Response-Header sind im Browser nicht verfügbar.

Lösung:

cors:
  expose_headers:
    - X-Request-ID        # Exponiere Header explizit!
    - X-RateLimit-Remaining

Client-Side:

response.headers.get('X-Request-ID')  // Jetzt verfügbar

Problem 6: CORS funktioniert nur auf einem Provider

Problem: CORS funktioniert auf Kong, aber nicht auf Envoy.

Lösung: Provider-spezifische Besonderheiten prüfen:

Kong:

# OPTIONS in Route-Methoden
methods: [GET, POST, OPTIONS]

APISIX:

# allow_credential (singular!)

Traefik:

# Middleware muss in Router referenziert sein

Envoy:

# CORS ist route-level, nicht global

Problem 7: Max-Age funktioniert nicht

Symptom: Preflight-Requests werden nicht gecacht.

Lösung:

# Setze max_age explizit
cors:
  max_age: 86400  # 24 Stunden

# Browser-Cache prüfen (DevTools → Network → Disable Cache)


Best Practice Checkliste

Vor Deployment

  • ✅ Wildcard * nur in Entwicklung verwenden
  • ✅ Spezifische Origins für Produktion
  • ✅ OPTIONS-Methode in allen Routes
  • ✅ HTTPS-Origins in Produktion
  • ✅ allow_credentials nur mit spezifischen Origins
  • ✅ Minimale allowed_headers
  • ✅ Nur benötigte expose_headers
  • ✅ Sinnvolle max_age (24 Stunden)

Security Review

  • ❌ Keine Wildcard mit Credentials
  • ❌ Keine sensitiven Headers exponiert
  • ❌ Keine HTTP-Origins in Produktion
  • ✅ CORS vor Authentication
  • ✅ Rate Limiting für OPTIONS-Requests

Testing

  • ✅ Preflight-Request testen (OPTIONS)
  • ✅ Actual Request testen (GET/POST)
  • ✅ Mit Credentials testen
  • ✅ Custom Headers testen
  • ✅ Verschiedene Origins testen
  • ✅ Browser DevTools prüfen

Zusammenfassung

CORS in GAL ermöglicht:

Einheitliche Konfiguration für alle Provider ✅ Vollständige CORS-Unterstützung (Origins, Methods, Headers, Credentials) ✅ Einfache Integration mit Authentication und Rate Limiting ✅ Provider-Abstraktion - schreibe einmal, deploye überall

Nächste Schritte: - Siehe AUTHENTICATION.md für CORS + Auth - Siehe HEADERS.md für zusätzliche Header-Manipulation - Siehe examples/cors-example.yaml für vollständige Beispiele

Hilfe benötigt? - Probleme melden: https://github.com/pt9912/x-gal/issues - Dokumentation: https://docs.gal.dev - Beispiele: examples/