
Technical write-up of CVE-2026-26717, an HMAC timing attack in OpenFUN Richie LMS webhook authentication, including vulnerable code, impact, and fix using hmac.compare_digest.
Author: Elvin Latifli
CVE ID: CVE-2026-26717
Severity: Medium (CVSS 3.1: 4.8 — AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N)
Affected Product: OpenFUN Richie (all versions prior to commit a1b5bbd)
Affected Component: src/richie/apps/courses/api.py — sync_course_runs_from_request
Fix Commit: a1b5bbda
Richie is an open-source CMS developed by France Université Numérique (OpenFUN), built on top of DjangoCMS. It is designed to help organizations build full-featured online education portals with course catalogs synchronized from Learning Management Systems (LMS) such as OpenEdX. Richie handles course data synchronization via authenticated webhooks — one of which contained the vulnerability described in this report.
The sync_course_runs_from_request webhook endpoint used the standard == operator to compare the incoming Authorization header against the expected HMAC signature:
# VULNERABLE
signature_is_valid = any(
authorization_header == get_signature(message, secret)
for secret in getattr(settings, "RICHIE_COURSE_RUN_SYNC_SECRETS", [])
)
The == operator performs a non-constant-time comparison — it short-circuits as soon as the first mismatching character is found. By sending many crafted requests and measuring response time differences, a remote attacker can deduce the correct signature one character at a time, ultimately forging a valid authentication token and bypassing the webhook's authentication entirely.
A successful attack allows an unauthenticated remote attacker to:
The fix replaces == with Python's hmac.compare_digest(), which is designed for constant-time string comparison and eliminates the timing side-channel:
# FIXED
signature_is_valid = any(
hmac.compare_digest(authorization_header, get_signature(message, secret))
for secret in getattr(settings, "RICHIE_COURSE_RUN_SYNC_SECRETS", [])
)