
Технический разбор CVE-2026-26717 — атаки по времени на HMAC в аутентификации вебхуков OpenFUN Richie LMS, включая уязвимый код, воздействие и исправление с помощью hmac.compare_digest.
Автор: Elvin Latifli
CVE ID: CVE-2026-26717
Серьёзность: Средняя (CVSS 3.1: 4.8 — AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N)
Затронутый продукт: OpenFUN Richie (все версии до коммита a1b5bbd)
Затронутый компонент: src/richie/apps/courses/api.py — sync_course_runs_from_request
Исправляющий коммит: a1b5bbda
Richie — это CMS с открытым исходным кодом, разработанная France Université Numérique (OpenFUN) на базе DjangoCMS. Она предназначена для помощи организациям в создании полнофункциональных порталов онлайн-образования с каталогами курсов, синхронизируемыми из систем управления обучением (LMS), таких как OpenEdX. Richie обрабатывает синхронизацию данных курсов через аутентифицированные вебхуки — один из них содержал уязвимость, описанную в этом отчёте.
Конечная точка вебхука sync_course_runs_from_request использовала стандартный оператор == для сравнения входящего заголовка Authorization с ожидаемой HMAC-подписью:
# УЯЗВИМО
signature_is_valid = any(
authorization_header == get_signature(message, secret)
for secret in getattr(settings, "RICHIE_COURSE_RUN_SYNC_SECRETS", [])
)
Оператор == выполняет сравнение не за постоянное время — он завершается досрочно, как только найден первый несовпадающий символ. Отправляя множество специально сформированных запросов и измеряя различия во времени ответа, удалённый злоумышленник может определить правильную подпись по одному символу за раз, в конечном итоге подделав действительный токен аутентификации и полностью обойдя аутентификацию вебхука.
Успешная атака позволяет неаутентифицированному удалённому злоумышленнику:
Исправление заменяет == на hmac.compare_digest() из Python, который предназначен для сравнения строк за постоянное время и устраняет побочный канал по времени:
# ИСПРАВЛЕНО
signature_is_valid = any(
hmac.compare_digest(authorization_header, get_signature(message, secret))
for secret in getattr(settings, "RICHIE_COURSE_RUN_SYNC_SECRETS", [])
)