OpenReception
Host-Header Injection in Confirmation Emails
generateBaseUrl builds the account-confirmation link from the request Host header, with no allowlist. The unauthenticated POST /api/auth/resend-confirmation rotates a pending invitee's confirmation token and emails a link whose host comes from the request, so a forged Host redirects the confirmation link to an attacker-controlled domain. If the invitee follows it, the one-time token reaches the attacker, who can claim the account before the invitee ever uses it. The sibling invite path already builds links from the trusted tenant.domain; this path does not. All releases through 1.1.1 are affected; fixed in 2.0.0.
Description
OpenReception is self-hosted, per-tenant appointment booking for medical practices, with end-to-end encryption between clients and staff. Staff and tenant administrators join through an invite that they confirm by following a one-time link emailed to them. We appreciate the project's work on a privacy-preserving booking design and the maintainers' engagement on this report.
The confirmation link's host comes from the request. generateBaseUrl returns the protocol, hostname, and port of the request URL, with no allowlist:
src/lib/server/email/email-service.ts:402-407
export function generateBaseUrl(url: URL): string { const protocol = url.protocol; const port = url.port ? `:${url.port}` : ""; const hostname = url.hostname; return `${protocol}//${hostname}${port}`;}sendConfirmationEmail builds the confirmation link from it: const confirmUrl = generateBaseUrl(requestUrl) + "/confirm/" + confirmationCode.
The endpoint that triggers this email is unauthenticated and forwards the request Host. POST /api/auth/resend-confirmation takes only an email and passes the request url straight through:
src/routes/api/auth/resend-confirmation/+server.ts:71-76
export const POST: RequestHandler = async ({ request, url }) => { const body = await request.json(); await UserService.resendConfirmationEmail(body.email, url); // ...};UserService.resendConfirmationEmail rotates the pending account's invite code to a fresh token valid for ten minutes, then emails the link built from that request url. The lookup is keyed by email in the central database, so a forged Host changes only the host in the generated link, not which account is targeted. A forged Host with a pending invitee's email therefore delivers a confirmation email whose confirmation link is https://attacker.example/confirm/<token>.
The sibling invite path builds its link from the trusted tenant.domain in production (invite/+server.ts:209); resend-confirmation passes the raw request url to generateBaseUrl, so the same trusted-domain source is not used. This is distinct from CVE-2026-48081, which concerned a javascript: link in the email footer; it is a separate behavior in the same email path.
Whether a spoofed Host reaches the application depends on the front proxy. The stock docker-compose.prod.yml sets no ORIGIN, and the shipped Caddyfile fronts the app with a :443 catch-all (reverse_proxy app:3000, on-demand TLS). The catch-all follows from the dynamic multi-tenant model: tenants add their own domains and Caddy issues certificates on demand, so the front block cannot statically enumerate tenant vhosts. A catch-all forwards an arbitrary upstream Host by default, and Caddy does not pin the Host to the TLS SNI, so a request that negotiates TLS with a valid tenant SNI and then carries a spoofed Host reaches the app with the attacker host intact. This delivery path is reasoned from the shipped Caddyfile and standard reverse_proxy behavior; the proof of concept targets the application directly and does not exercise the proxy layer.
An operator who instead configures explicit per-tenant proxy blocks with no catch-all turns the proxy into a Host allowlist and is not affected; that configuration is incompatible with the on-demand tenant-domain feature and is not the stock setup.
Once the attacker holds the one-time token, the account is claimed through the application's own confirm and passkey-registration flow on the real tenant domain, which the attacker reaches normally. The attacker registers a passkey they control and logs in, obtaining a full session as the invitee before the invitee has ever used the account. The login step spoofs nothing: only the resend-confirmation request carries the forged Host.
Impact
- The attack requires the victim to be a pending invitee who has not yet registered a passkey, who follows the emailed link, on an instance whose front proxy forwards the spoofed
Host(the stock catch-all does). Given that, an attacker takes over the invitee's account at the role it was invited with. The realistic target is a staff or tenant-admin invitee mid-onboarding: claiming the account first and registering an attacker-controlled passkey grants the attacker a full session in their place. From that session the attacker holds the invitee's authority over the tenant, which for a staff account includes the tenant's appointment data and the staff-only directory the role can access. - The people affected are the invited staff member, whose account is taken over before they ever use it, and the practice's clients, whose appointment records the hijacked session can read and, depending on the role, cancel, alter, or delete. The attacker-controlled link arrives in a routine onboarding email generated by the practice's own server, from its normal sender address, so the invitee has little signal to distrust it.
Mitigation
Upgrade to OpenReception 2.0.0, which resolves this advisory; 2.0.0 is a major release that adds a required MANAGEMENT_DOMAIN environment variable, so review the release notes before upgrading. The underlying fix is to build the confirmation link from a trusted domain rather than the request Host, as the invite path already does (invite/+server.ts:209); a single ORIGIN is not a clean fit for a multi-domain, multi-tenant deployment. As defense in depth, the front proxy can be configured to pass only known Host values upstream, though that is incompatible with on-demand tenant domains and so is secondary to the application-level change. Operators who cannot upgrade immediately can have an invited staff member confirm the link's domain through a separate channel before following it, and a practice administrator can revoke and re-invite pending accounts, which rotates the confirmation token and invalidates any token already captured.
Severity Reasoning
Host header.PR:NNo account or privileges; the endpoint is unauthenticated and the target is named by the invitee's email address.UI:RThe pending invitee must open the confirmation email and click its link for the one-time token to reach the attacker.S:UImpact stays within the instance's authorization scope.C:HThe hijacked session reads the tenant's staff-only data, including the appointment calendar and staff directory the role can access.I:HThe session can alter, cancel, or delete the appointment records the role is permitted to manage.A:LThe hijacked session can remove individual appointment records the role manages, a bounded availability impact short of the wholesale destruction seen where staff key shares are deleted.Confidentiality and Integrity are High on the tenant data the hijacked session can reach; Availability is Low, reflecting a bounded effect on the appointment records the role manages.
References
How We Can Help
Who We Are
The security researchers behind this advisory.

Dr. rer. nat. Simon Weber
Senior Pentester & MedSec Researcher
I evaluate your SaMD with the same industry-defining security insight I contributed to the BAK MV for the revision of the B3S standard.
- PhD on Hospital Cybersecurity
- Critical vulnerabilities found in hospital systems
- Alumni of THB MedSec Research Group
- gematik Security Hero

Dipl.-Inf. Volker Schönefeld
Senior Application Security Expert
As a former CTO and developer turned pentester, I work alongside your team to uncover vulnerabilities and find solutions that fit your architecture.
- 20+ years as CTO, 50M+ app downloads
- Architected and secured large-scale IoT fleets
- Certified Web Exploitation Specialist
- gematik Security Hero
Looking for a Penetration Test?
Machine Spirits specializes in security assessments for medical devices and healthcare IT. From MDR penetration testing to C5 cloud compliance, we help MedTech companies meet regulatory requirements.
