BadPhish: A Safety Net for the Links You Click in Your Inbox

Chris Child | 2026-09-08 | 5 min read

It was a phishing simulation, timed to land during a real P1. The email looked exactly like the kind of thing you'd expect in the middle of an incident — urgent, plausible, asking you to click. A huge number of people did. I was one of the ones who hesitated, but only just.

That's the moment I realised how thin the line is between "I know better" and "I clicked." Training helps, but training is a memory you have to recall in the split second before your thumb hits the link. What I wanted was a safety net that sits at the moment of the click — something that shows you what's actually behind the link before your browser loads it.

So I built BadPhish.

What BadPhish does

BadPhish is a browser extension that intercepts link clicks in Outlook on the web and Gmail. When you click a link, the extension sends the clicked URL to the BadPhish server, where all the analysis happens. If the link looks suspicious, your browser stops on a warning page — you see the destination, the signals that were found, and the provider checks, and you decide whether to continue.

The goal is simple: give you as much information about the link as possible, and an honest read on whether it's safe. Not a wall of jargon — the facts you need to make the call.

Why the server does the thinking

The extension is deliberately thin. It captures the click and forwards the URL; it doesn't try to judge anything locally. That's a design choice, not an accident.

Keeping the analysis server-side means the heuristics can evolve without shipping a new extension version. It means the extension never needs broad permissions or a background service worker. And it means the verdict logic lives in one place, where it's testable and auditable, instead of being scattered across every browser that runs it.

The trade-off is privacy, which is why the privacy model is the part I care most about.

What we see — and what we don't

For a security tool, the privacy posture is the whole pitch. If BadPhish asked for access to your whole mailbox to do its job, it wouldn't be worth it.

  • Only clicked URLs are sent. When you click a link, the URL goes to the server for analysis. Nothing else about your mail or browsing is collected.
  • No mailbox or browsing access. The extension never sends email content, attachments, page contents, or browsing history. No analytics, no tracking pixels.
  • Short-lived records. Warning tokens and their URL records expire within an hour. No selling data, no advertising, no profiles.

That last point is worth unpacking, because it's the most interesting design decision in the whole project.

The token design

The warning page never carries the raw URL. When the server checks a link, it mints an opaque, short-lived token that encodes the verdict and the destination. The browser is redirected to badphish.app/link/{token}, and the warning page renders from that token — not from a URL sitting in the address bar or the query string.

Why does that matter? Because the URL itself is hostile input. It can contain secrets, tokens, email addresses, or customer data. Putting it in the path or query string would leak it into logs, history, and referrers. The token keeps the destination server-side, opaque, and expiring — so the raw URL never has to travel through the warning flow.

It also means the warning page can't be tricked into redirecting to an arbitrary destination. The token is the only thing that authorises the proceed action, and the server never accepts a destination URL directly.

The warning page

The warning page is where all of this becomes visible. It leads with a one-line verdict — derived from the record, not a static opener — then lists the signals worst-first, in plain language.

The signals come from a few places:

  • Local heuristics in the shared analysis package — typosquatting and brand proximity, homoglyph "reads as" normalization, non-Latin and invisible character checks, punycode domains, suspicious ports, URL shorteners, encoded nested URLs.
  • Provider checks — currently OpenPhish and Wayback first-seen site history, which tell you whether a domain has been reported or how long it's been around.

A domain registered two days ago, a known URL shortener hiding the destination, link text that names a different domain than the one it points to — those are the signals that show up, each with a plain-language severity label. The safe action (go back) is the primary button; proceeding is an explicit, secondary choice. Known-bad links are blocked outright.

Where it stands

BadPhish is a proof of concept I built for myself, and it's still under active development. Today it protects Outlook on the web and Gmail in Chrome, with an Edge listing on the way. The Chrome Web Store listing is submitted and should be public soon.

If you're a developer and want to try it before then, the repo is on GitHub — npm run dev builds the extension and launches a browser with it loaded, pointed at Outlook or Gmail. Feedback is very welcome.

The safety net

The P1 phishing simulation caught a lot of people because the email was good — well-timed, plausible, urgent. No amount of training makes you immune to that. What training can't do is pause the moment and show you what's behind the link.

That's the gap BadPhish is for. It won't replace security awareness, and it's not a substitute for your organization's other controls. It's a safety net at the moment of the click — the extra second of information that turns "I clicked" into "I looked first."

Comments

Related Posts