CVE-2026-54510: Speakr: CSRF bypass via unauthenticated API token parameter in csrf_exempt_for_api_tokens hook
Speakr is a personal, self-hosted web application designed for transcribing audio recordings. Prior to 0.8.21-alpha, the csrfexemptforapitokens() beforerequest hook in src/app.py calls csrf.exempt(viewfunc), permanently adding the selected view to Flask-WTF's process-global exemption set. The istokenauthenticated() function in src/utils/tokenauth.py calls extracttokenfromrequest() and treats any present token, including request.args.get('token'), as authenticated without hashing the token, querying the database, or checking validity. A network-reachable attacker can therefore send a false token to disable CSRF protection for the targeted view for the worker lifetime. Because the exemption applies to the view function across HTTP methods, a cross-origin GET to /account with a query token can poison CSRF state for a later state-changing POST without triggering CORS preflight. This browser sequence requires attacker-controlled content on a sibling subdomain under the documented cookie conditions. The bypass can modify profile data, custom prompts, transcription settings, preferences, and administrative status through routes such as admintoggleadmin. The changepassword route also skips current-password verification when currentuser.password is empty, allowing the chain to set a local password on an SSO-only account and bypass SSO. This issue is fixed in version 0.8.21-alpha.
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
Speakrto a version that resolves this vulnerability.Fixed in 0.8.21-alpha - Compensating control
Ensure the attacker cannot execute cross-origin requests with attacker-controlled query tokens against Speakr (e.g., prevent sibling-subdomain token poisoning under the documented cookie conditions by tightening cookie/domain scope and cross-site request exposure).
Event History
Frequently Asked Questions
Which deployments are realistically exposed to cross-site exploitation?
The described browser attack requires attacker-controlled content on a sibling subdomain and the documented cookie conditions. The attacker must also be able to reach the Speakr instance over the network.
Does an attacker need a valid API token or an existing account?
No valid token is required for the CSRF bypass described: any token supplied in the request, including the token query parameter, is treated as authenticated without validation. The affected state-changing requests rely on a victim's authenticated browser session.
Why can a seemingly harmless GET request affect later requests?
A cross-origin GET to /account with a false query token can add the targeted view to Flask-WTF's process-global CSRF exemption set. That exemption persists for the worker lifetime and applies to the view across HTTP methods, enabling a later state-changing POST without CSRF protection.
What impact can the bypass have?
It can modify profile data, custom prompts, transcription settings, preferences, and administrative status through affected routes such as admin_toggle_admin. The description indicates that the password-change route is excluded or otherwise limited, but its full behavior is not provided.