Mar 2, 2026·5 min read·2 visits
Discourse improperly handled email login links by authenticating users immediately upon an HTTP GET request to the token URL. This allowed automated systems (like email scanners) to consume the one-time token, breaking the login process for legitimate users and potentially establishing sessions unintentionally. The fix requires an intermediate confirmation screen requiring a POST request.
A critical authentication vulnerability exists in Discourse versions prior to 2.3.0 and 2.4.0.beta3. The vulnerability arises from a failure to implement a confirmation screen during the email login process, where clicking a magic link immediately authenticates the user via a GET request. This behavior violates HTTP idempotency principles, allowing email security scanners, link pre-fetchers, and potentially malicious scripts to inadvertently trigger authentication and invalidate the single-use login token before the user can intentionally access the application.
Discourse, a widely deployed open-source community platform, supports passwordless authentication via "magic links" sent to a user's email address. In affected versions (stable releases prior to 2.3.0 and beta releases prior to 2.4.0.beta3), the platform failed to implement a confirmation step for these login requests. The vulnerability is classified under CWE-287 (Improper Authentication).
When a user requests a login email, Discourse generates a cryptographically secure, single-use token embedded in a URL. In the vulnerable configuration, navigating to this URL via a standard HTTP GET request triggers the session#email_login controller method, which immediately validates the token, logs the user in, and establishes a session cookie. This design flaw treats a safe, idempotent retrieval method (GET) as a state-changing action, exposing the authentication flow to unintentional triggering by external agents.
The root cause of CVE-2019-1020018 is the violation of HTTP semantics regarding GET request idempotency. According to RFC 7231, GET requests should only retrieve representation of data and must not result in state-changing side effects. The Discourse application mapped the /session/email-login/:token route directly to the authentication logic.
Specifically, the SessionController was configured to skip Cross-Site Request Forgery (CSRF) protection and XMLHttpRequests (XHR) checks for the email_login action. This was achieved via the skip_before_action filter in the Rails controller. Consequently, any User-Agent—be it a web browser, a search engine crawler, or an enterprise email security scanner—that requested the URL would successfully execute the login logic. Because the token is strictly single-use, an automated fetch by a security scanner would "burn" the token. When the actual user subsequently clicked the link, the token would be invalid, resulting in a failed login attempt.
The remediation involved refactoring the authentication flow to require explicit user interaction via an HTTP POST request. This aligns the process with security best practices, ensuring that state changes (login) cannot occur via simple navigation.
1. Routing Changes (config/routes.rb)
The route definition was split. The GET request now maps to an informational action, while the actual login logic is reserved for a POST request.
# BEFORE: Single route for action
get "session/email-login/:token" => "session#email_login"
# AFTER: Split routes for confirmation flow
get "session/email-login/:token" => "session#email_login_info"
post "session/email-login/:token" => "session#email_login"2. Controller Logic (app/controllers/session_controller.rb)
The controller filters were updated to remove email_login from the exclusion list for XHR checks, re-enabling standard Rails protections. A new method email_login_info was introduced to render the confirmation UI.
# Vulnerable Controller Configuration
skip_before_action :check_xhr, only: %i(sso sso_login ... email_login)
def email_login
token = params[:token]
user = EmailToken.confirm(token)
if user
log_on_user(user) # Immediate login on GET
return redirect_to path("/")
end
# ... error handling
endIn the patched version, email_login is removed from skip_before_action, and the logic changes to ensure the user explicitly clicks a button (triggering a POST) to complete the session establishment.
While this vulnerability allows for unauthorized state changes, the primary impact observed in the wild is a Denial of Service (DoS) against the authentication mechanism rather than direct account takeover. However, specific attack vectors exist:
Scenario 1: Token Consumption (DoS) Enterprise email environments often deploy security appliances (e.g., Proofpoint, Microsoft Defender) that "detonate" or pre-fetch links in incoming emails to check for malware. When such a scanner receives the Discourse login email, it issues a GET request to the link. The Discourse server processes this as a valid login, establishes a session for the scanner (which is immediately discarded), and invalidates the token. When the legitimate user attempts to click the link, they receive an "expired or invalid token" error.
Scenario 2: Unintentional Session Establishment
If an attacker can coerce a user's browser to pre-fetch the URL (e.g., via a <link rel="prefetch"> tag or an <img> tag in a context where the token is known or leaked), the user's browser would silently establish a session. This bypasses the user's intent to log in at that specific moment, though it requires the attacker to possess the token, which typically implies they already have access to the victim's email.
The vulnerability holds a CVSS v3.1 score of 7.3 (High), driven primarily by the Integrity and Availability impacts, alongside the complete lack of complexity required to trigger it.
The vulnerability does not inherently allow an attacker to generate tokens for arbitrary users; it strictly mishandles the validation lifecycle of legitimate tokens.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
Discourse Discourse | < 2.3.0 | 2.3.0 |
Discourse Discourse | 2.4.x < 2.4.0.beta3 | 2.4.0.beta3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-287 |
| Attack Vector | Network |
| CVSS Score | 7.3 (High) |
| EPSS Score | 0.00294 (0.29%) |
| Impact | Session Management / Denial of Service |
| Exploit Status | No Known Public Exploit |
Improper Authentication