Forums Bug Reports Thread

Course offer convert-to-learn and convert-to-moodle forms always fail CSRF (wrong field name AND wrong session key) — both admin actions unusable

Patrick Bass · Jun 6 · 25 · 1 Locked
[Critical] [Urgent] [Bug Fixed] [Always Reproduces]
🚀 OP Jun 6, 2026 8:34pm

Area: Admin deep-dive (commerce/config) (audit p15b) · Surface: /admin/courses/offers/{id}/edit (convert delivery forms) · Dimension: Law 12 / functional correctness (CSRF token name+source mismatch) · Severity: critical

Both delivery-conversion admin actions for course offers are completely broken from the UI. The hidden CSRF input uses the wrong field name and pulls from a session key that is never populated, so validateCsrf() rejects every submission with a 403. The correct pattern (used everywhere else in the same file, e.g. form.php:98) is `name="_csrf_token" value="<?= $e($csrfToken) ?>"`.

Evidence

templates/admin/courses/form.php:422 and :431 emit `<input type="hidden" name="_csrf" value="<?= htmlspecialchars($_SESSION['csrf'] ?? '', ...) ?>">`. But BaseController.php:491 reads the token from `$_POST['_csrf_token']` (or HTTP_X_CSRF_TOKEN), and the real session token lives in `$_SESSION['csrf_token']` (BaseController.php:425-428), not `$_SESSION['csrf']`. So the field has BOTH the wrong name (`_csrf` vs `_csrf_token`) AND an empty value (`$_SESSION['csrf']` is never set). AdminCourseSalesController.php:457 (convertToLearn) and :503 (convertToMoodle) both call $this->validateCsrf() first thing. validateCsrf() (BaseController.php:489-501) does `hash_equals($this->csrfToken(), $_POST['_csrf_token'] ?? '')` -> mismatch -> http_response_code(403); echo 'Invalid or missing security token. Please refresh and try again.'; exit. These are native form POSTs (no fetch/XHR interception). Routes are live: routes.php:2082-2083. Every click of 'Convert to mobieusLearn' or 'Revert to Moodle delivery' returns a bare 403 page.

Suggested fix. Replace both inputs with the standard `<input type="hidden" name="_csrf_token" value="<?= $e($csrfToken) ?>">` (the template already has $csrfToken available at form.php:27 and uses it correctly at line 98). Grep the file for any other `name="_csrf"` occurrences.

Filed by the automated tenant-app audit and adversarially evidence-verified. Status: verified. Open — not yet actioned.


Patrick Bass
@mobieus

🚀 Jun 6, 2026 8:47pm

Resolved — fixed and deployed. Commit a6963d71efe8, shipped dev-first then to all tenants on 2026-06-06.

The convert-to-learn and convert-to-moodle offer forms now emit the canonical _csrf_token field sourced from $csrfToken. They previously used a non-existent _csrf field with the wrong $_SESSION['csrf'] key, so validateCsrf() always failed (403). Both admin actions work again.

Status: fixed. Thread closed and locked.


Patrick Bass
@mobieus

Log in or register to reply to this thread.