Forums Bug Reports Thread

State-changing email confirmation performed over GET with no CSRF token (CourseCatalog/Verification confirm-email)

Patrick Bass · Jun 6 · 15 · 1 Locked
[Minor] [Normal Priority] [Bug Fixed] [Always Reproduces]
🚀 OP Jun 6, 2026 7:06pm

Area: Monetization (audit p10) · Surface: GET /account/verified/confirm-email (VerificationController@confirmEmail) · Dimension: security · Severity: minor

The email-confirmation endpoint mutates verification state (sets email_verified_at, clears the token) on a GET request with no CSRF protection. It is gated by a 64-hex-char token (`bin2hex(random_bytes(32))`, VerificationController.php:184) so entropy makes forgery impractical today, which is why this is minor rather than major. Still, performing a state change on GET is the classic CSRF/prefetch hazard: link prefetchers, email-client image proxies, and antivirus URL scanners can silently fire the confirmation, and any future reduction in token entropy or token logging turns this into a forgeable state change. OWASP A01/A05 (security misconfiguration: unsafe method for a mutation).

Evidence

routes.php:1349 `$router->get('/account/verified/confirm-email', 'VerificationController@confirmEmail');`. VerificationController.php:247-301 performs a write on GET: `PlatformAdminClient::doPost('/api/verification/update', ['id' => $vid, 'email_verified_at' => date(...), 'email_verify_token' => null]);` (lines 264-268). No `validateCsrf()` is called (it cannot be — GET handlers receive no CSRF middleware: the only CSRF-bearing group is the POST/auth group at routes.php:775).

Suggested fix. Confirm via a POST form (with the standard `_csrf_token`) reached from a GET landing page, or keep the GET but treat the token as single-use and short-TTL and explicitly mark the confirmation idempotent. At minimum document that the token is the sole authorization and ensure it is never written to logs/Referer.

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


Patrick Bass
@mobieus

🚀 Jun 7, 2026 5:44am

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

Hardened GET /account/verified/confirm-email (confirmEmail). Added a redactToken() helper and applied it to the exception path's logger->error call so the request URL's ?token= (and any bare 64-hex token) can never leak into logs. Added a doc block recording that the single-use token is the sole authorization, is consumed on first success (email_verify_token nulled + email_verified_at stamped), and that replay is idempotent — matching the audit fix's 'at minimum' requirement.

Status: fixed. Thread closed and locked.


Patrick Bass
@mobieus

Log in or register to reply to this thread.