Repository navigation
sec(auth): CORS wildcard origin + credentials=true on staging Lambda URL #390
Description
Summary
The staging Lambda Function URL is configured with allowed_origins = ["*"] (wildcard) in terraform/environments/aws/github-staging.tfvars while simultaneously setting Access-Control-Allow-Credentials: true via the cors { allow_credentials = true } block in the Lambda Terraform module (terraform/modules/compute/aws/lambda/main.tf). AWS Lambda Function URLs enforce CORS at the infrastructure layer before the Go application code runs: an Origin request header causes AWS to emit Access-Control-Allow-Origin: <reflected-origin> + Access-Control-Allow-Credentials: true in every response, regardless of what the Go handler sets. Dynamic probing confirms this: curl -H "Origin: https://attacker.io" https://33pz7pombdqwu3bdlxp4lqxyra0bsriy.lambda-url.us-east-1.on.aws/api/health returns both the configured origin AND the attacker's origin, each with Access-Control-Allow-Credentials: true.
Per the CORS specification, a browser will forward cookies/credentials to a cross-origin response only when Access-Control-Allow-Origin exactly matches the request's Origin AND Allow-Credentials is true. With a wildcard origin the spec says browsers must not expose credentials — but this deployment is not using the literal * response value; AWS is reflecting the inbound Origin verbatim, which turns it into a fully-trusted origin. Any website on the internet can therefore issue fetch("https://33pz7pombdqwu3bdlxp4lqxyra0bsriy.lambda-url.us-east-1.on.aws/api/users", {credentials:"include"}) and the browser will attach the victim's session token, bypassing the CSRF defence.
Risk: full account takeover if a CUDly admin can be tricked into visiting an attacker-controlled page while logged in. Staging is used by the CI pipeline and by engineers validating changes before production — the attack surface is real.
Location
terraform/environments/aws/github-staging.tfvarsline 28:lambda_allowed_origins = ["*"]terraform/modules/compute/aws/lambda/main.tf:cors { allow_credentials = true; allow_origins = var.allowed_origins }
Reproduction / PoC
# Confirm reflected Origin + Allow-Credentials: true
curl -s -i -X OPTIONS \
-H "Origin: https://attacker.io" \
-H "Access-Control-Request-Method: POST" \
"https://33pz7pombdqwu3bdlxp4lqxyra0bsriy.lambda-url.us-east-1.on.aws/api/auth/login" \
| grep -i "access-control"
# Returns:
# Access-Control-Allow-Origin: https://attacker.io ← reflected
# Access-Control-Allow-Credentials: true
# Cross-origin credential read PoC (browser):
# fetch("https://33pz7pombdqwu3bdlxp4lqxyra0bsriy.lambda-url.us-east-1.on.aws/api/auth/me",
# { credentials: "include" })
# .then(r => r.json())
# .then(console.log) // leaks full user profileSuggested fix
- Replace
lambda_allowed_origins = ["*"]with the actual CloudFront/dashboard origin in every environment's tfvars. For staging this should be the staging CloudFront domain or the Lambda URL itself:lambda_allowed_origins = ["https://33pz7pombdqwu3bdlxp4lqxyra0bsriy.lambda-url.us-east-1.on.aws"]
- Add a Terraform validation rule to the
allowed_originsvariable that rejects["*"]whenallow_credentials = true:validation { condition = !(contains(var.allowed_origins, "*") && var.function_url_allow_credentials) error_message = "Wildcard origin is incompatible with allow_credentials = true." }
- Consider adding an integration test that hits the Lambda preflight with a random origin and asserts the response does NOT reflect it.
Severity rationale
CORS wildcard + credentials = any-origin cross-site request forgery without a CSRF token. While the Go application also enforces per-session CSRF tokens for state-changing requests, the reflected-origin response means a cross-origin browser fetch with credentials: "include" reads the session token from a prior JSON response (e.g., /api/auth/me) and can then supply its own CSRF header — full bypass. Priority P1 because staging is actively used by the CI pipeline and by engineers.
Activity
sec(iac): reject wildcard CORS origin, set explicit origins per env (c…
sec(iac): Lambda Function URL auth hardening -- CORS wildcard rejecti…
cristim commentedon May 28, 2026
Verified live + in code: PR #519 (commit 2313366, closes #390) + PR #574 (commit f65998a) both merged into origin/feat/multicloud-web-frontend. Live check: OPTIONS request with Origin: https://attacker.io returns 200 with no Access-Control-Allow-Origin reflected (no longer echoing arbitrary origin). lambda_allowed_origins = ["*"] replaced with explicit per-env CloudFront origins.