Flask AppBuilder 中长期弃用的 OpenID 认证方法漏洞

最近,Islam Rzayev 让我们注意到了 Flask AppBuilder 中长期弃用的 OpenID 认证方法存在一个漏洞。该漏洞允许恶意用户通过伪造一个特殊构造的请求并实现自己的 OpenID 服务来冒充任何 Airflow UI 用户身份。尽管这是一种旧的、已弃用且几乎不使用的认证方法,我们仍然认真对待了这个问题。

此问题 ONLY 影响在其 webserver_config.py 文件中将 AUTH_TYPE 设置为 AUTH_OID 的用户。这是一种非常旧且已弃用的认证方法,几乎没有人使用。

我们建议即使是少数仍在使用此认证方法的用户立即采取行动,要么升级到 Apache Airflow 2.8.2,要么切换到另一种认证方法(如果他们无法立即执行上述任一操作,则应用我们提供的变通方法)。

需要强调的是,由于许多用户可能会对名称感到困惑,OpenID 与 OpenID Connect **不是**一回事。它们是完全不同的协议,OpenID Connect(也称为 OIDC)是一种现代、广泛使用的协议,而 OpenID 是一种遗留协议,已在 10 多年前被弃用,从那时起几乎被社区中的所有人放弃,包括 Flask AppBuilder 示例服务中所有支持它的服务,因此极不可能有人仍在继续使用它。

由于这种极不可能的配置,Flask AppBuilder CVE 的严重性仅为“Moderate”(中等),而非“Critical”(严重)。它影响的用户数量非常少(甚至可能没有),也不太可能成为攻击目标。但是,我们仍然建议仍在使用 AUTH_OID 的用户采取补救措施。

此漏洞已在 Flask Appbuilder 4.3.11 中修复,而 Apache Airflow 2.8.2 使用该版本的 Flask Application Builder。我们建议仍在使用此认证方法的用户要么切换到另一种认证方法,要么升级到 Apache Airflow 2.8.2。如果他们无法快速执行上述任一解决方案,则应应用下面提供的变通方法。

影响

当 Flask-AppBuilder 将 AUTH_TYPE 设置为 AUTH_OID 时,它允许攻击者伪造 HTTP 请求,欺骗后端使用任何请求的 OpenID 服务。如果攻击者部署了一个自定义 OpenID 服务并且该服务可被后端访问,则此漏洞可能授予攻击者未经授权的特权访问。

此漏洞仅在应用程序使用 OpenID(而非 OpenID Connect,即 OIDC)时才可被利用。目前,此协议被视为遗留协议,使用率已显著降低。

可能的补救措施

  • 更改您的认证方法 - 如果您正在使用 AUTH_OID,几乎没有商业服务支持它,它在 10 年前被弃用,并在 4 年前几乎被社区中的所有人放弃。您最好的选择是选择另一种认证方法。
  • 升级到 Apache Airflow 2.8.2(这也将升级到包含修复程序的 Flask-AppBuilder 4.3.11)
  • 如果无法升级,应用下面的变通方法

变通方法

如果无法升级或更改认证方法,请将以下内容添加到您的 webserver_config.py 文件中以解决此问题

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

致谢

特别感谢 Islam Rzayev 负责任地发现并报告了此问题,并感谢 Daniel Gaspar 在此问题上密切合作,并与 Apache Superset(同样使用 Flask AppBuilder)协调了披露事宜。

分享

延伸阅读

Airflow Summit 2022

Jarek Potiuk

2022 年 Airflow Summit 大会在此举行

开发 Apache Airflow 就像使用 "Breeze" 一样轻松愉快

Jarek Potiuk

一位首席软件工程师关于开发者生产力的历程。了解 Jarek 和他的团队如何为社区加速和简化 Airflow 的开发。