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

最近,Islam Rzayev 让我们注意到 Flask AppBuilder 中长期已弃用的 OpenID 认证方式存在漏洞。该漏洞允许恶意用户通过伪造特制请求并实现自己的 OpenID 服务,进而冒充任意 Airflow UI 用户的身份。虽然这是一种已废弃且几乎不再使用的认证方式,但我们仍然严肃对待此问题。

此问题仅影响在其 webserver_config.py 文件中将 AUTH_OID 设置为 AUTH_TYPE 的用户。这是一种非常古老且已弃用的认证方式,几乎不会被任何人使用。

我们建议即便是仍在使用此认证方式的少数用户也应立即采取行动,要么升级到 Apache Airflow 2.8.2,要么切换到其他认证方式(如果他们无法立即完成上述任一操作,可使用我们提供的解决方案)。

需要强调的是,许多用户可能会因为名称混淆而误解:OpenID 并不等同于 OpenID Connect。这两者是完全不同的协议,OpenID Connect(亦称 OIDC)是现代且广泛使用的协议,而 OpenID 是一种已在十多年前被废弃的遗留协议,自此几乎被社区所有成员放弃,包括 Flask AppBuilder 示例服务中支持它的所有服务,因此几乎不可能还有人在使用它。

由于这种极不可能的配置,Flask AppBuilder CVE 的评级仅为“中等”,而非“严重”。受影响的用户数量极少(甚至可能没有),且不太可能成为攻击目标。然而,我们仍建议仍在使用 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,几乎没有商业服务支持它,它在十年前已被弃用,并在四年前被社区几乎所有人抛弃。最佳做法是选择其他认证方式。
  • 升级到 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 在此事上密切合作,并与使用 Flask AppBuilder 的 Apache Superset 一同协调披露。

分享

阅读更多

Airflow Summit 2022

Jarek Potiuk

Airflow Summit 2022 已经开启

开发 Apache Airflow 如“清风”般轻松

Jarek Potiuk

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