Skip to content

Hard-coded CORS allowlist includes a likely typo origin (github-spy.etlify.app) + fails-open on missing Origin #479

@ionfwsrijan

Description

@ionfwsrijan

📜 Description

The backend CORS policy is implemented as a hard-coded allowlist in backend/server.js, but it currently includes https://github-spy.etlify.app (likely meant to be netlify.app). Because credentials: true is enabled, any origin that matches the allowlist is allowed to make credentialed cross-origin requests to the auth API.

If that origin is not actually controlled by the project (typo / stale domain / later becomes attacker-controlled), this becomes a high-impact security footgun: an untrusted site could interact with /api/auth/* using the victim’s cookies and read responses.

This is also operationally risky: the real deployed frontend origin may be blocked (login/signup silently fail), pushing maintainers to “temporarily” relax CORS and accidentally introduce an actual wildcard CORS vulnerability.

Where it happens (code pointers)

  • backend/server.js
    • const allowedOrigins = ['http://localhost:5173', 'https://github-spy.etlify.app'];
    • credentials: true
    • origin: function (origin, callback) { if (!origin || allowedOrigins.indexOf(origin) !== -1) callback(null, true); ... }

Why this is critical

1) Credentialed cross-origin trust is extremely sensitive

Because sessions are cookie-based (express-session) and CORS allows credentials, any mistakenly trusted origin can:

  • send authenticated requests (logout, any future authenticated endpoints),
  • read the JSON responses (CORS allows the browser to expose them),
  • become a stepping stone for account enumeration / session abuse.

2) “Domain drift” / typo turns into a latent security vuln

Hard-coding a production origin string means:

  • renames or redeploys can break auth flows,
  • “quick fixes” often lead to opening up CORS broadly,
  • typos can silently create trust in the wrong origin.

Steps to reproduce (conceptual)

  1. Deploy backend with current CORS config.
  2. Host a frontend at the allowed origin (the one listed in allowedOrigins).
  3. From that origin, issue fetch(..., { credentials: "include" }) calls to:
    • POST /api/auth/login
    • GET /api/auth/logout
  4. Observe: browser is allowed to send cookies and read responses due to allowlisted origin + credentials: true.

If the allowlisted domain is not actually the project’s frontend, this creates an avoidable trust boundary.

Expected behavior

  • Production allowed origins are:
    • correct (no typos),
    • configurable per environment,
    • tightly scoped (no “fail open”),
    • tested.

Actual behavior

  • Allowed origins are hard-coded and include a suspicious/likely incorrect domain.
  • The origin callback accepts requests with missing Origin (unnecessary for a browser-only API, and makes policy harder to reason about).

Proposed fix (non-trivial, robust)

  1. Move CORS config to environment:
    • ALLOWED_ORIGINS as a comma-separated list
    • strict parsing + normalization (scheme + hostname + port)
  2. Remove !origin fail-open for production (or explicitly separate “non-browser clients” behind an API token).
  3. Add automated tests for CORS:
    • allowed origin gets Access-Control-Allow-Origin echo + Access-Control-Allow-Credentials: true
    • disallowed origin is rejected
    • missing Origin behavior matches intended policy
  4. Document deployment:
    • specify the canonical frontend domain
    • explain how to set ALLOWED_ORIGINS safely
  5. (Optional) Add CSRF protection if any state-changing authenticated endpoints are added beyond login/logout.

Acceptance criteria

  • Backend does not ship with a hard-coded production origin.
  • Only explicitly configured origins can make credentialed cross-origin calls.
  • Tests prevent regressions/typos in origin allowlisting.

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions