Security
Working: Magic Link Tokens Live in Your Logs — And TOTP Has a Second Endpoint
Davi DEV Community
3 views
Passwordless auth was supposed to eliminate credential phishing. Instead, it moved the credential from a password field into a URL, a log file, and an email inbox.
Magic links encode auth tokens in URLs that end up in server logs and Referer headers. TOTP verification endpoints are rarely protected by the same rate limiter as the password endpoint. Passwordless auth replaces one attack surface with three classes of vulnerability.
The Token IS the Password — And It Lives in a URL
A magic link token is functionally equivalent to a plaintext password delivered over email, encoded in a GET request. The token carries three pieces of information: user identity, an expiry timestamp, and an HMAC signature. A single string grants full authentication with no second factor required.
HTTP GET requests log the full URL, including query string or path segment, in server access logs by default. Any process with access to those logs can extract active tokens. CVE-2026-39912 (CVSS 9.1) shows the extreme case: V2Board's loginWithMailLink API returned the token directly in the HTTP response body, accessible to any caller without authentication. Over 7,000 internet-facing instances were exposed at the time of disclosure.
Your Access Logs Already Contain Valid Auth Tokens
nginx logs the full URL of every GET request. A typical access.log entry:
203.0.113.42 - - [04/Sep/2026] "GET /auth/verify?token=eyJhbGci... HTTP/1.1" 200 1842
The token persists until log rotation, readable by any process with access to /var/log/nginx/access.log. Apache, Caddy, and CDN proxies like Cloudflare store the same data in different formats; the token appears in all of them.
The Referer header compounds the exposure. When a user clicks an outbound link from the verification page, the browser sends the full URL in the Referer header of the next request. Third-party analytics like Google Analytics and Hotjar log that header in their own systems. HackerOne #855618 (Shopify) documented a concrete case. The magic link token was passed as a parameter via a Branch.io deeplink, interceptable by any app registered for that URL scheme.
Email security gateways like Microsoft Defender, Mimecast, and Proofpoint prefetch URLs in emails for threat scanning. For single-use tokens, the gateway consumes the token before the legitimate user clicks. The user receives an error page; the token was delivered to the scanner, not the recipient. The fix requires Referrer-Policy: no-referrer on the landing page and server-side token consumption before any redirect.
Email Forwarding Rules Make Magic Links a Persistence Mechanism
An attacker with initial inbox access can create a silent forwarding rule that routes all future magic link tokens to an attacker-controlled address. The rule survives a password reset and forced logout of all sessions.
The attack chain: phishing or credential stuffing opens inbox access. The attacker creates a forwarding rule, requests a magic link, and authenticates with the received token. After detection and password reset, the attacker continues receiving future magic links because password resets do not revoke email forwarding configurations. Gmail and Outlook forwarding rules do not appear in standard security notifications.
The application logs magic link sends but not the email delivery path or forwarding state. The compromise is invisible to the application until the attacker acts on a received token. No login attempts fail; no alerts fire.
TOTP Brute Force Works Because Verification Is a Separate Endpoint
APIs typically rate-limit the password login endpoint but verify OTP on a separate route that inherits none of those protections. The result is a direct brute-force path against a 6-digit keyspace, 10^6 combinations.
GHSA-cwhc-53hw-qqx6 (Outline) documents the pattern precisely: OTP generated at /auth/email, verified at /auth/email.callback, no attempt limit on the callback endpoint. The advisory documents two additional bypasses. The JWT cookie was decoded without signature verification, allowing an attacker to forge a JWT with an arbitrary user ID and reset the per-user counter. Injecting X-Forwarded-For: 127.0.0.1 reset the IP-based limit in misconfigured deployments.
At 10 req/s against a 6-digit OTP keyspace (10^6), coverage in 1 hour is 3.6%, basic probability against 6-digit OTPs. HackerOne #1060541 (MTN Group) found the /nim/submit OTP submission endpoint with zero rate limiting, the same pattern documented in the Outline advisory.
RFC 6238 Permits 90-Second Validity — And Most Libraries Default to It
RFC 6238 recommends accepting at most one adjacent 30-second time step to handle clock drift. With the default ±1 step window, 3 codes are valid simultaneously: t-1, t, and t+1. Brute-force success probability per request triples compared to a strict window.
MediaWiki defaulted to ±4 windows, resulting in 9 simultaneously valid codes at any instant. RFC 6238 requires verifiers to reject second use of the same OTP via a server-side used-code store; most libraries omit this control. The drift parameter rarely appears in library security documentation, meaning teams select a default without evaluating the effect on the attack surface. With default drift and no used-code store, an attacker at 10 req/s has a 90-second window with 3 valid targets per round.
SMS OTP Is Intercepted at the Carrier Level, Not Just via SIM Swap
The Salt Typhoon compromise (2024) proved that SMS OTP interception does not require social engineering a carrier support representative. Chinese state-affiliated actors compromised infrastructure at AT&T, Verizon, T-Mobile, and Lumen Technologies, capturing unencrypted SMS including OTP codes at scale.
CISA published its "Mobile Communications Best Practice Guidance" in December 2024 with explicit instruction: "Do not use SMS as a second factor for authentication. SMS messages are not encrypted." UK Cifas 2025 Fraudscape recorded a 1,055% increase in SIM swap incidents in 2024, from 289 to nearly 3,000 cases. SS7/Diameter protocol attacks allow silent SMS interception without any carrier employee involvement.
Closing the Three Surfaces: Specific Controls Per Attack Vector
Magic links: maximum 15-minute expiry; server-side single-use token store; deliver the token in a POST body, not a URL path or query string. This eliminates log exposure. Set Referrer-Policy: no-referrer on the landing page before any redirect. Audit and remove inbox forwarding rules after any account compromise; rules survive password resets and forced session termination.
OTP verification endpoints: rate-limit independently of the password endpoint; per-account lockout after N failures, not just per-IP; bind the issued code to a device fingerprint or session ID, not email alone.
TOTP time windows: set allowedClockDrift=0 in the authenticator library where NTP sync is reliable; maintain a per-user used-code store; alert on more than 3 consecutive verification failures. The MAGO Intel tool (intel.mago.team) detects endpoints lacking rate limiting via response timing differences, identifying exposed OTP and magic link surfaces before attackers find them.
The long-term exit is passkeys (FIDO2): phishing-resistant by design, no OTP surface, no token in a URL. For SMS, deprecate in new integrations and migrate existing users to TOTP or passkeys.
A passwordless flow that stores tokens in GET URLs and skips per-account OTP limits is not more secure than a password. It is a credential with a shorter shelf life and more ways to leak it.
Read original: https://dev.to/roxdavirox/working-magic-link-tokens-live-in-your-logs-and-totp-has-a-second-endpoint-52l2
← Previous
The SDK That Passes Your Tests and Exfiltrates Your Credentials
Next →
I Built a Version Bump Tool in Rust That Is 10,000x Faster Than Its Python Counterparts.
Related
🇮🇳 Building OmniCore OS: From an Idea to a Working Development Build:
Security
2
DEV Community
HMAC Proves Origin, Not Freshness: Replay Attacks Against Signed APIs
Security
3
DEV Community
JWT kid Parameter Attacks: SQL Injection and Path Traversal via Key ID
Security
2
DEV Community
CSWSH: Four Major WebSocket Frameworks Default to Vulnerable While Attackers Get a Bidirectional Channel
Security
2
DEV Community
Comments0
No comments yet — be the first