All articles Industry

Protecting Patient Portals From Account Takeover

A patient portal holds some of the most sensitive data a person has: diagnoses, medications, test results, insurance details and messages with clinicians. It is also, for most users, an account they open a few times a year with a password they set once and reused elsewhere. That combination makes portals an attractive target for account takeover, and an awkward one to defend, because the people using them are often older, unwell, or acting on behalf of someone else.

This post covers the main takeover paths into patient portals and how device recognition and risk-based step-up address them while collecting as little data as possible. It is about security engineering. It does not describe any compliance certification, and you should map these controls to your own regulatory obligations with your compliance team.

How portal accounts get taken over

Credential stuffing

Attackers take username and password pairs leaked from other breaches and try them against your login at scale. Because portal passwords are frequently reused and rarely changed, some fraction succeed. The traffic usually comes from automation spread across many IPs, often through residential proxies so it looks like ordinary home connections.

Recovery-flow takeover

When the password does not work, attackers use the “forgot password” flow instead. If recovery relies on an email account the attacker has already compromised, or on a phone number they have taken over through SIM swapping, the reset hands them the account. Recovery flows are often less protected than login, because they are built to help people who are locked out.

Phishing and session theft

Patients receive convincing messages about appointments or test results with links to lookalike login pages. Captured credentials, and sometimes session cookies, are used soon after from a different device.

Misused delegated access

Many portals support caregiver or proxy access: a parent for a child, an adult child for an aging parent. This is legitimate and valuable, but it means a single account may be used from several devices, and a single device may be used for several accounts. Rules designed for consumer apps, such as “one account per device,” would break it.

Device recognition as the base layer

Most of these attacks share one feature: the attacker uses a device the account has never used. A patient who logs in from the same tablet every few months has a very stable device history. A credential-stuffing tool, a phishing kit’s operator or a SIM-swapper does not.

Identify the device at login and recovery, and verify the event on your server:

// login page
const prynt = await Prynt.load({ apiKey: 'pk_live_…' });
const { requestId } = await prynt.identify({ tag: { action: 'login' } });
// send requestId with the login form
// server, after the password check succeeds
const event = await prynt.getEvent(requestId);           // @prynt/node, secret key
const knownDevice = await db.trustedDevices.exists(patientId, event.visitorId);

if (event.decision === 'block') return deny();           // automation, tampering, burned device
if (!knownDevice || event.decision === 'challenge') {
  return requireSecondFactor();                          // step up, don't lock out
}
return signIn();

After a successful login, attach the account with updateEvent(requestId, { linkedId: patientId }) and record the device as trusted if the user completed the second factor. Over time each account accumulates a small set of known devices, and anything outside it is asked for more. The new device login detection post covers how to manage that list, including expiry.

Stopping credential stuffing before the password check

Stuffing tools are automation, and automation has signals that appear before the password is even tested: BOT, AUTOMATION_BEHAVIOR, TLS_AUTOMATION, DATACENTER, RESIDENTIAL_PROXY, and VELOCITY when one device cycles through many usernames. A single device attempting logins for many different accounts is the clearest tell of all. Failed attempts are never linked to an account, so count distinct attempted usernames per visitorId in your own login log; for successful sign-ins you link, accountsOnDevice and the ACCOUNT_SHARING signal show the same pattern.

Act on these before checking the password where you can. Refusing a request without revealing whether the credentials were valid denies the attacker the confirmation they need. For borderline traffic, a proof-of-work challenge through prynt.challenge() adds cost to every attempt without asking patients to solve a puzzle. The credential stuffing detection post goes deeper.

Hardening the recovery flow

Treat recovery as at least as sensitive as login:

  • Identify the device on the recovery page. A reset requested from a device the account has used before is far less risky than one from a new device behind a proxy.
  • Delay or add friction for risky resets. For a new device with risk signals, require an additional check, such as a code to a previously verified channel or a verification step through your support team, before the new password takes effect.
  • Notify on every reset. Send a notice to all verified channels, with a clear “this wasn’t me” path.
  • Watch for impossible travel. A reset completed from a location far from the account’s recent activity, with IMPOSSIBLE_TRAVEL, warrants a hold.

The account recovery fraud prevention post covers these patterns in detail.

Respect caregiver access

Do not treat multiple accounts on a device as fraud by default in a portal. A caregiver’s laptop that holds their own account and their parent’s is normal. What distinguishes it from stuffing is scale and behavior: a handful of accounts used steadily over months, from a device with no automation signals, versus many accounts tried in minutes. If your portal supports formal proxy access, the cleanest solution is to model it explicitly so the caregiver signs in once and switches between patients, rather than logging in to each account separately.

Minimize what the fraud layer sees

Fraud prevention does not require health information, so design the integration so none reaches it.

  • Opaque linkedId. Use an internal account id, never a medical record number, name or email.
  • Neutral tags. { action: 'login' } is enough. Do not tag events with appointment types, departments or anything clinical.
  • Clean URLs. Events record the page URL. If your portal puts identifiers or clinical context in paths or query strings, identify only on pages that do not, such as login and recovery, or fix the URLs.
  • IP minimization. Depending on deployment configuration, Prynt can store IP addresses in full, truncated, or not at all, while still using them at decision time.
  • Retention and erasure. Retention follows your plan, and data can be erased by visitorId or linkedId.
  • Privacy signals. Global Privacy Control is honored by default, and a consent mode is available if your legal basis for these signals is consent.

The privacy page, the DPA and the trust page describe the processing terms and current security practices. Review them against your obligations before go-live.

A sensible order of work

Start with device identification on login and recovery, logging decisions without acting. After a few weeks, enable step-up for new devices and hard blocks for clear automation. Then harden recovery. The account takeover page summarizes the signals involved. The protection most patients notice should be a second-factor prompt on a new device, and nothing else.

Try it free

Prynt is device intelligence with a free tier — visitor IDs, bot & fraud Smart Signals, and behavioral biometrics, powered by a cross-site network. Start free.

Keep reading