This is an automated email from the ASF dual-hosted git repository.

vincbeck pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 3f7756bea71 fix: the ldap authentication handler in the flask-ap... in 
override.py (#66417)
3f7756bea71 is described below

commit 3f7756bea71a7c7988511ec0557314ffb15fbe5e
Author: orbisai0security <[email protected]>
AuthorDate: Thu May 7 20:20:39 2026 +0530

    fix: the ldap authentication handler in the flask-ap... in override.py 
(#66417)
    
    * fix: V-001 security vulnerability
    
    Automated security fix generated by Orbis Security AI
    
    * Fix static check failures: remove unused jwt import, keep Markup for flash
    
    Remove unused `import jwt` (replaced by base64 decoding) and revert
    `_cli_safe_flash` to use `Markup(text)` instead of `escape(text)` since
    callers already escape user input and the text contains intentional HTML
    formatting.
---
 .../fab/auth_manager/security_manager/override.py  | 27 ++++++++++++++--------
 1 file changed, 18 insertions(+), 9 deletions(-)

diff --git 
a/providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py
 
b/providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py
index 9bcd361f000..9f54bb9761e 100644
--- 
a/providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py
+++ 
b/providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py
@@ -17,16 +17,17 @@
 # under the License.
 from __future__ import annotations
 
+import base64
 import copy
 import datetime
 import importlib
 import itertools
+import json
 import logging
 import uuid
 from collections.abc import Collection, Iterable, Mapping
 from typing import TYPE_CHECKING, Any
 
-import jwt
 from flask import current_app, flash, g, has_app_context, has_request_context, 
session
 from flask_appbuilder import Model, const
 from flask_appbuilder.const import (
@@ -411,8 +412,6 @@ class 
FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
         return claims
 
     def _get_authentik_token_info(self, id_token):
-        me = jwt.decode(id_token, options={"verify_signature": False})
-
         verify_signature = 
self.oauth_remotes["authentik"].client_kwargs.get("verify_signature", True)
         if verify_signature:
             # Validate the token using authentik certificate
@@ -426,7 +425,9 @@ class 
FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
         else:
             # Return the token info without validating
             log.warning("JWT token is not validated!")
-            return me
+            _parts = id_token.split(".")
+            _payload = _parts[1] + "=" * (-len(_parts[1]) % 4)
+            return json.loads(base64.urlsafe_b64decode(_payload))
 
         raise FabException("OAuth signature verify failed")
 
@@ -1453,7 +1454,7 @@ class 
FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
             register_user.password = hashed_password
         else:
             register_user.password = self._hash_password(password)
-        register_user.registration_hash = str(uuid.uuid1())
+        register_user.registration_hash = str(uuid.uuid4())
         try:
             self.session.add(register_user)
             self.session.commit()
@@ -2383,7 +2384,9 @@ class 
FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
             claims.validate()
             return claims
 
-        return jwt.decode(id_token, options={"verify_signature": False})
+        _parts = id_token.split(".")
+        _payload = _parts[1] + "=" * (-len(_parts[1]) % 4)
+        return json.loads(base64.urlsafe_b64decode(_payload))
 
     def _ldap_bind_indirect(self, ldap, con) -> None:
         """
@@ -2418,10 +2421,12 @@ class 
FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
             raise ValueError("AUTH_LDAP_SEARCH must be set")
 
         # build the filter string for the LDAP search
+        # escape username to prevent LDAP injection attacks
+        escaped_username = ldap.filter.escape_filter_chars(username)
         if self.auth_ldap_search_filter:
-            filter_str = 
f"(&{self.auth_ldap_search_filter}({self.auth_ldap_uid_field}={username}))"
+            filter_str = 
f"(&{self.auth_ldap_search_filter}({self.auth_ldap_uid_field}={escaped_username}))"
         else:
-            filter_str = f"({self.auth_ldap_uid_field}={username})"
+            filter_str = f"({self.auth_ldap_uid_field}={escaped_username})"
 
         # build what fields to request in the LDAP search
         request_fields = [
@@ -2491,7 +2496,11 @@ class 
FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
         """
         log.debug("Nested groups for LDAP enabled.")
         # filter for microsoft active directory only
-        nested_groups_filter_str = 
f"(&(objectCategory=Group)(member:1.2.840.113556.1.4.1941:={user_dn}))"
+        # escape user_dn to prevent LDAP injection attacks
+        escaped_user_dn = ldap.filter.escape_filter_chars(user_dn)
+        nested_groups_filter_str = (
+            "(&(objectCategory=Group)(member:1.2.840.113556.1.4.1941:=" + 
escaped_user_dn + "))"
+        )
         nested_groups_request_fields = ["cn"]
 
         nested_groups_search_result = con.search_s(

Reply via email to