Vulnerability in long deprecated OpenID authentication method in Flask AppBuilder

Recently Islam Rzayev made us aware of a vulnerability in the long deprecated OpenID authentication method in Flask AppBuilder. This vulnerability allowed a malicious user to take over the identity of any Airflow UI user by forging a specially crafted request and implementing their own OpenID service. While this is an old, deprecated and almost not used authentication method, we still took the issue seriously.

This issue ONLY affects users who have AUTH_OID set in their webserver_config.py file as AUTH_TYPE. This is a very old and deprecated authentication method that is unlikely to be used by anyone.

We would like to advise even the small number of our users that still use this authentication method to take an immediate action and either upgrade to Apache Airflow 2.8.2 or switch to another authentication method (or apply a workaround we provide if they cannot do either of the above immediately).

Important to stress, because many of the users might get confused by the name, OpenID is NOT the same as OpenID Connect. Those are completely different protocols and while OpenID Connect (also known as OIDC) is a modern, widely used protocol, OpenID is a legacy protocol that has been deprecated more than 10 years ago and since then has been abandoned by almost everyone in the community, including all services in Flask AppBuilder example services that supported it, so it is highly unlikely someone is still using it.

Due to this highly unlikely configuration the Flask AppBuilder CVE is just “Moderate” not “Critical”. It affects a very small (if any) number of users and it’s not likely to be a target for an attack. However, we still advise our users who still use AUTH_OID to apply remediation.

This vulnerability is fixed in Flask Appbuilder 4.3.11 and Apache Airflow 2.8.2 uses that version of Flask Application Builder. We advise users who still use this authentication method to either switch to another authentication method or upgrade to Apache Airflow 2.8.2. If they cannot do either of these solutions quickly, they should apply the workaround provided below.

Impact

When Flask-AppBuilder is set to AUTH_TYPE set to AUTH_OID, it allows an attacker to forge an HTTP request that could deceive the backend into using any requested OpenID service. This vulnerability could grant an attacker unauthorised privilege access if a custom OpenID service is deployed by the attacker and accessible by the backend.

This vulnerability is only exploitable when the application is using OpenID (not OpenID Connect also known as OIDC). Currently, this protocol is regarded as legacy, with significantly reduced usage.

Possible remediation

  • Change your authentication method - if you are using AUTH_OID, there are almost no commercial services supporting it, it was deprecated 10 years ago and abandoned by nearly everyone in the community 4 years ago. Your best choice is to choose a different authentication method.
  • Upgrade to Apache Airflow 2.8.2 (which also upgrades to Flask-AppBuilder 4.3.11 that contains a fix)
  • If upgrade is not possible, apply the workaround below

Workarounds

If upgrade or changing authentication method is not possible add the following to your webserver_config.py file to fix the issue:

import os

from flask import flash, redirect
from flask_appbuilder.security.forms import LoginForm_oid
from flask_appbuilder.security.views import AuthOIDView
from flask_appbuilder.views import expose

from airflow.www.security import AirflowSecurityManager

basedir = os.path.abspath(os.path.dirname(__file__))

class FixedOIDView(AuthOIDView):
    @expose("/login/", methods=["GET", "POST"])
    def login(self, flag=True):
        form = LoginForm_oid()
        if form.validate_on_submit():
            identity_url = None
            for provider in self.appbuilder.sm.openid_providers:
                if provider.get("url") == form.openid.data:
                    identity_url = form.openid.data
            if identity_url is None:
                flash(self.invalid_login_message, "warning")
                return redirect(self.appbuilder.get_url_for_login)
        return super().login(flag=flag)

class FixedAirflowSecurityManager(AirflowSecurityManager):
    authoidview = FixedOIDView

SECURITY_MANAGER_CLASS = FixedAirflowSecurityManager

Credits

Big thanks to Islam Rzayev for finding out and reporting the issue responsibly and to Daniel Gaspar for very close cooperation on this one and coordinating the disclosure together with the Apache Superset where Flask AppBuilder is also used.

Share

Read also

Airflow Summit 2022

Jarek Potiuk

Airflow Summit 2022 is here

It's a "Breeze" to develop Apache Airflow

Jarek Potiuk

A Principal Software Engineer's journey to developer productivity. Learn how Jarek and his team sped up and simplified Airflow development for the community.