Safeguard
Security

Flask-CORS Security: The 2024 CVEs and How to Configure It Safely

Flask-CORS is easy to enable and easy to misconfigure. A look at the 2024 path-matching CVEs and the configuration mistakes that actually open your API.

Aisha Rahman
Security Analyst
6 min read

Flask-CORS is the standard way to add cross-origin resource sharing headers to a Flask API, and its security depends almost entirely on configuration, though a cluster of 2024 CVEs in version 4.0.1 means you also need to keep the package current. CORS itself is a browser mechanism that relaxes the same-origin policy in a controlled way. The failure mode with flask-cors is rarely the protocol; it is a developer typing CORS(app) with no arguments, shipping it, and unknowingly allowing every origin on the internet to make credentialed requests.

Here is what the library does, what went wrong in 2024, and how to set it up so it protects rather than exposes you.

What Flask-CORS Does

The flask-cors extension inspects incoming requests and adds the appropriate Access-Control-Allow-* response headers, and it handles CORS preflight OPTIONS requests for you. You can apply it globally or per-route.

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)

# Scoped: only these origins, only the API path
CORS(
    app,
    resources={r"/api/*": {"origins": ["https://app.example.com"]}},
    supports_credentials=True,
)

That is roughly what a safe configuration looks like: a specific origin allowlist, scoped to the paths that actually need cross-origin access. Contrast it with the configuration that shows up in tutorials and then in production:

CORS(app)  # allows all origins on every route

With no arguments, the extension defaults to Access-Control-Allow-Origin: *. On a public read-only endpoint that is sometimes acceptable. On an API that serves authenticated user data, it is a hole.

The Cardinal Sin: Wildcard Origin With Credentials

The single most dangerous CORS misconfiguration is combining a permissive origin with credentialed requests. The CORS spec forbids the literal combination of Access-Control-Allow-Origin: * and Access-Control-Allow-Credentials: true, because that would let any site read authenticated responses. Browsers enforce that.

The trap is that developers work around the restriction by reflecting the request's Origin header back verbatim while also setting credentials to true. That is functionally "allow any origin, with cookies," and it defeats the same-origin policy entirely. Any malicious page a logged-in user visits can then make authenticated calls to your API and read the responses. If you set supports_credentials=True, you must pair it with an explicit, finite origin allowlist. Never with a wildcard, and never with blind origin reflection.

The 2024 Flask-CORS CVEs

Version 4.0.1 of flask-cors accumulated a set of disclosed vulnerabilities in 2024, all rooted in how it matched request paths against configured rules. These are worth knowing because they broke the assumption that a correctly written rule actually protected the endpoint you thought it did.

CVE-2024-6844 covers inconsistent CORS matching caused by the request path being run through unquote_plus, which turns a + character into a space and can normalize a path in a way that mismatches your configured rule.

CVE-2024-6839 describes improper regex path matching, where the extension could prioritize a longer regex pattern over a more specific one, resulting in a less restrictive policy being applied to a sensitive endpoint than intended.

CVE-2024-6866 is a case-sensitivity flaw in request path matching that could let a request to a differently-cased path evade the rule meant to restrict it.

Separately, CVE-2024-6221 concerns the Access-Control-Allow-Private-Network header being settable to true in a way that could expose services on a private network, and CVE-2024-1681 is a log-injection issue.

The common thread: these are matching and header bugs, not something you can fully configure around. The remediation is to upgrade to a patched release, so pin flask-cors to the current version rather than an old 4.0.x. Do not treat "I wrote a strict rule" as sufficient on a version where the matcher itself was buggy. Always confirm the fixed version against the PyPA advisory database or GitHub advisories for your exact release before you ship.

A Configuration Checklist

A few rules keep flask-cors on the safe side of the line.

Enumerate origins explicitly. Use a concrete list of trusted origins, never "*" on anything credentialed and ideally not even on public endpoints where you can name the callers.

Scope by resource. Apply CORS only to the paths that need it via the resources argument, rather than blanketing the whole app. An admin or internal endpoint should not be advertising cross-origin access at all.

Constrain methods and headers. Set methods and allow_headers to the minimum your frontend uses. A GET-only public API has no business allowing DELETE cross-origin.

Be deliberate about credentials. Only set supports_credentials=True when the frontend genuinely sends cookies or auth headers cross-origin, and when it is set, treat the origin allowlist as a security control you review.

Keep the package pinned and patched. Given the 2024 history, flask-cors is a dependency where the version matters as much as the config.

Testing Your CORS Policy

Do not trust the config file; test the running behavior. A quick curl against a preflight tells you what the server actually returns:

curl -i -X OPTIONS https://api.example.com/api/data \
  -H "Origin: https://evil.example" \
  -H "Access-Control-Request-Method: GET"

If the response echoes Access-Control-Allow-Origin: https://evil.example for an origin you never allowlisted, you have a reflection problem. Run this from an origin you know should be rejected, as part of CI if you can, so a regression in the policy fails a build rather than shipping.

Because the risk is split between your configuration and the library's own version, cover both. An SCA tool such as Safeguard can flag when your pinned flask-cors version carries one of the disclosed advisories, which catches the half of the problem that no amount of careful configuration addresses. For the header and policy side, our security academy walks through CORS behavior with worked examples.

FAQ

Is CORS(app) with no arguments safe in Flask?

Usually not. It defaults to allowing all origins, which is only acceptable on genuinely public, non-credentialed, read-only endpoints. For anything serving authenticated data, specify an explicit origin allowlist scoped to the relevant paths.

Which Flask-CORS version has the 2024 vulnerabilities?

The disclosed path-matching and header CVEs (including CVE-2024-6844, CVE-2024-6839, and CVE-2024-6866) affect version 4.0.1. Upgrade to a patched release and verify the fixed version against the official advisory for your version.

Can I use a wildcard origin with credentials in Flask-CORS?

No. The CORS spec forbids Access-Control-Allow-Origin: * together with credentials, and reflecting the origin back to fake a wildcard is equally unsafe. Use a finite allowlist whenever supports_credentials is enabled.

How do I test my Flask-CORS configuration?

Send a preflight OPTIONS request with an untrusted Origin header using curl and inspect the returned Access-Control-Allow-Origin. If a disallowed origin is reflected back, the policy is misconfigured. Automate this check in CI to catch regressions.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.