Signed Is Not Requested: Apache Airflow CVE-2026-86467 and the Login-CSRF Class Airflow Already Fixed Once

At 03:17:16Z on 20 October 2026 NVD published CVE-2026-86467, assigned by the Apache Software Foundation, CWE-352, rated moderate by the project. The affected component is apache-airflow-providers-amazon before 9.38.0, specifically the AWS auth manager’s SAML login flow. The announcement went to the Airflow users list over Jarek Potiuk’s name, crediting Andrew Rukin (Arenadata) as finder and Potiuk as remediation developer.

The defect, in the advisory’s words: “the AWS auth manager’s SAML login flow does not bind the authentication response to the browser that began it. The login handler discards the AuthnRequest ID and the callback validates the assertion without requiring a matching InResponseTo, so an unauthenticated attacker who can cause a victim to load a page can complete a SAML login in the victim’s browser using the attacker’s own assertion, silently logging the victim into the attacker’s Airflow account. Subsequent work the victim performs lands in a session the attacker controls.”

Why this is worse than it scores

Login-CSRF reads like a curiosity — you are logged into someone else’s account, which sounds like the attacker’s problem. In Airflow it is not, because of what Airflow is. An orchestration operator who believes they are in their own instance will create connections, paste credentials into them, upload DAG configuration, set variables and read task logs. Every one of those actions lands in a session the attacker owns and can later log into. The advisory’s last sentence is the whole impact statement: subsequent work the victim performs lands in a session the attacker controls. Airflow Connections are, for most organisations, a concentrated store of database passwords, cloud credentials and API keys for exactly the data and model pipelines this site covers — so a phishing-grade prerequisite (get a victim to load a page) converts into credential collection with the victim doing the typing.

Note what is not broken. The identity provider signed the assertion correctly; python3-saml verified that signature correctly; the user in the response is genuinely that user. Nothing cryptographic failed. The missing check is the one nobody writes down, and the fix commit states it better than we can. From the merged code:

“This cookie is the browser’s half of the binding: it states that this browser asked for these AuthnRequests. A SAML assertion is signed by the identity provider, which authenticates the identity in the response — it says nothing about which browser asked. Without this, an assertion issued for one login would be accepted in a browser that never started it, signing that browser in as the assertion’s subject.”

That is the same distinction we pulled out of Gitea’s CVE-2026-104627 two days ago, where an unsolicited OpenID 2.0 assertion linked an attacker identity to a signed-in victim: authentication proves who, and a separate, unglamorous piece of state has to prove that this session asked. Two protocols, two codebases, one missing question. When a flaw shows up twice in a week across unrelated projects, it is a class, not an accident.

Airflow fixed this exact class in April

The advisory ends with a sentence the project did not have to include, and it is the most useful line in the record: “This is the same login-CSRF class fixed in the Keycloak auth manager as CVE-2026-40948.” That record, published 18 April 2026, describes apache-airflow-providers-keycloak failing to generate or validate the OAuth 2.0 state parameter and not using PKCE, letting an attacker with a realm account deliver a crafted callback that logs a victim into the attacker’s Airflow session — and it names the same consequence, that “any credentials the victim subsequently stored in Airflow Connections would be harvestable by the attacker.”

So: six months and two days between fixing login-CSRF in one Airflow auth manager and fixing it in the sibling auth manager in the same repository, for the same impact, with a different protocol spelling of the same missing binding. That is the finding worth taking away. Airflow’s pluggable auth managers are parallel implementations of one security-critical flow, and a fix applied to one of them is not a fix applied to the pattern. Credit where it is due for naming the repetition in the advisory rather than quietly shipping the second patch — most projects do not, and the class is invisible if nobody writes the sentence. But if you maintain parallel implementations of an authentication handshake, the lesson is to treat a CVE in one as an audit ticket against all of them on the day it lands, not a bug in one file.

What the fix does

PR apache/airflow#73698, “Bind the AWS auth manager SAML response to the browser that started the login”, merged to main on 4 October 2026 at 22:09:08Z — nine files, +627/−73, of which the route itself is +181/−19 and its tests +305/−54. The mechanism is a short-lived signed cookie, _awsam_login_state, holding one entry per login the browser has started and not completed. Entries are HMAC-SHA256 signed with the API server secret key (chosen, per an in-code comment, because that key is already required to be identical across API servers, so a login can start on one instance and finish on another), carry a per-entry expiry with a LOGIN_STATE_MAX_AGE of 600 seconds, and are capped at MAX_PENDING_LOGINS = 5 so that opening a second tab does not invalidate the first while still bounding the cookie. The callback then validates the SAML response against an AuthnRequest ID this browser actually registered; a response that matches nothing gets “No login in progress for this browser. Start the login from Airflow and try again.”

Two details deserve flagging. The fix also pins the login mode server-side rather than trusting RelayState — the PR notes RelayState “only selected the return mode and arrived in the same form, so the response could choose that too,” which is a second, smaller instance of trusting attacker-reachable data from the response leg. And IdP-initiated login, which is definitionally unbindable because no AuthnRequest exists, is preserved behind a new explicit configuration key that defaults to off. That is the right default and the right shape: the insecure mode remains available to deployments that genuinely need it, but you have to say so.

What to do

Upgrade apache-airflow-providers-amazon to 9.38.0, published to PyPI on 17 October 2026. Only deployments using the AWS auth manager with SAML authentication are affected; if you use a different auth manager, check which one and whether it has had this audit. Note the sequencing if you are reconstructing a timeline: the fix merged 4 October, the release shipped 17 October, the CVE published 20 October — the patch was installable for three days before the advisory told anyone why to install it, which is the normal and correct order, but it also means a 9.38.0 upgrade you already did silently closed this.

After upgrading, do not leave allow_idp_initiated_login enabled unless an identity provider genuinely requires it. Then treat the exposure window as a credential question rather than a patching one: if the AWS auth manager with SAML has been reachable by your users, review Airflow Connections created or modified during that window for credentials that may have been entered into an attacker-held session, and rotate what you cannot account for. The victim-side signal is subtle by design — a user who finds themselves logged in as someone else, or logged in without having clicked login, is reporting this bug. Not in the CISA KEV catalogue (checked directly, catalogue version 2026.10.16, 1,748 entries; neither CVE-2026-86467 nor CVE-2026-40948 is listed); no exploitation has been reported.

Our verification was read-only. The CVE identifier, NVD publication timestamp of 2026-10-20T03:17:16.813Z, vulnStatus, the security@apache.org source identifier, the full description, the CWE-352 assignment, the absence of a CVSS vector at NVD and the reference list were read first-hand from the NVD 2.0 API. The complete advisory text, the moderate severity rating, the affected version statement and the finder and remediation credits were read first-hand from the Apache Airflow users-list announcement thread via the lists.apache.org API. The pull request title, merge timestamp, base branch, changed-file list and per-file line counts, the quoted body text, and every quoted code comment, constant name and value — COOKIE_NAME_LOGIN_STATE, LOGIN_STATE_MAX_AGE of 600, MAX_PENDING_LOGINS of 5, the HMAC-SHA256 signing with the API server secret key, the NO_LOGIN_IN_PROGRESS message and the new IdP-initiated-login configuration key — were read first-hand from the merged diff of apache/airflow#73698 via the GitHub API. The 9.38.0 PyPI upload timestamp of 2026-10-17T18:10:46Z and its position as the latest release were read first-hand from the PyPI JSON API. The CVE-2026-40948 description and its 2026-04-18 publication date were read first-hand from the NVD 2.0 API. KEV status was checked directly against the published catalogue. Brave Search was used for discovery only. We installed nothing, stood up no Airflow instance, configured no identity provider, issued no SAML assertion and executed nothing; no third-party request was sent beyond reading the cited public records.

Sources: