Skip to content

XBlock custom auth does not respect JWT Scopes

Moderate
feanil published GHSA-qx8m-mqx3-j9fm Jan 12, 2024

Package

edx-platform

Affected versions

>= Ironwood

Patched versions

Palm

Description

Context

XBlocks have enabled JWT authentication via its own custom code here:

# In this case, we are using Session based authentication, so we need to check CSRF token.
if request.user.is_authenticated:
error = CsrfViewMiddleware(get_response=lambda request: None).process_view(request, None, (), {})
if error:
return error
# We are reusing DRF logic to provide support for JWT and Oauth2. We abandoned the idea of using DRF view here
# to avoid introducing backwards-incompatible changes.
# You can see https://github.com/openedx/XBlock/pull/383 for more details.
else:
authentication_classes = (JwtAuthentication, BearerAuthenticationAllowInactiveUser)
authenticators = [auth() for auth in authentication_classes]
for authenticator in authenticators:
try:
user_auth_tuple = authenticator.authenticate(request)
except APIException:
log.exception(
"XBlock handler %r failed to authenticate with %s", handler, authenticator.__class__.__name__
)
else:
if user_auth_tuple is not None:
request.user, _ = user_auth_tuple
break

This code does not respect JWT scopes, and someone with a JWT with more limited scopes could call this endpoint when they are not supposed to.

Our JWT scope code is somewhat brittle. It relies on this middleware code to add a scope related permission class if no other scope related permission class is found. It then relies on DRF permissions to enforce this permission.

A solution might be to have custom code that makes use of that permission as well.

Ideally, our auth code (which is quite complex) would all live in edx-drf-extensions and this custom code for enabling JWT authentication (or DRF Authentication and permissions) on a Django request, would live with the rest of the other auth code so individual services don't implement custom solutions.

Aside: this custom code happened to cause an unexpected bug because it is authenticating using a Django request object, rather than a DRF request object.

Impact

Someone with a JWT with more limited scopes could call this endpoint when they are not supposed to.

Patches

Has the problem been patched? What versions should users upgrade to?

Workarounds

Is there a way for users to fix or remediate the vulnerability without upgrading?

References

  • # In this case, we are using Session based authentication, so we need to check CSRF token.
    if request.user.is_authenticated:
    error = CsrfViewMiddleware(get_response=lambda request: None).process_view(request, None, (), {})
    if error:
    return error
    # We are reusing DRF logic to provide support for JWT and Oauth2. We abandoned the idea of using DRF view here
    # to avoid introducing backwards-incompatible changes.
    # You can see https://github.com/openedx/XBlock/pull/383 for more details.
    else:
    authentication_classes = (JwtAuthentication, BearerAuthenticationAllowInactiveUser)
    authenticators = [auth() for auth in authentication_classes]
    for authenticator in authenticators:
    try:
    user_auth_tuple = authenticator.authenticate(request)
    except APIException:
    log.exception(
    "XBlock handler %r failed to authenticate with %s", handler, authenticator.__class__.__name__
    )
    else:
    if user_auth_tuple is not None:
    request.user, _ = user_auth_tuple
    break

Severity

Moderate
6.4
/ 10

CVSS base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Changed
Confidentiality
Low
Integrity
Low
Availability
None
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N

CVE ID

CVE-2024-22209

Weaknesses

No CWEs