Phishing email checker
The goal
I wanted a tool that catches the two phishing signals I’d learned about on the Learn page, mismatched sender domains and urgent pressure language, instead of just reading about them.
What you need
Python 3 (the re module is built in), and about two to three hours. This project is a step up from the first two: it needs string pattern matching, not just counting characters.
How I built it
The checker does three things to a block of pasted email text: it scans for a short list of pressure phrases (“verify your account”, “act now”), it pulls out any brand name mentioned (PayPal, Amazon, Microsoft) and compares it against the domains actually linked in the email, and it does a rough check for lookalike domains, ones that are nearly identical to a real one but with a character swapped.
import re
URGENT_PHRASES = [
“verify your account”, “act now”, “your account will be suspended”,
“click immediately”, “urgent action required”, “confirm your identity”,
]
KNOWN_BRANDS = {“paypal”: “paypal.com”, “amazon”: “amazon.com”, “microsoft”: “microsoft.com”}
def extract_domain(url):
match = re.search(r’https?://([^/]+)’, url)
return match.group(1).lower() if match else None
def looks_like_lookalike(domain, real_domain):
if domain == real_domain or abs(len(domain) – len(real_domain)) > 2:
return False
diffs = sum(1 for a, b in zip(domain, real_domain) if a != b)
return diffs <= 2
def check_email(text):
text_lower = text.lower()
findings = []
for phrase in URGENT_PHRASES:
if phrase in text_lower:
findings.append(f’Pressure language found: “{phrase}”‘)
links = re.findall(r'https?://[^\s"\'>]+', text)
domains_found = {extract_domain(l) for l in links if extract_domain(l)}
for brand, real_domain in KNOWN_BRANDS.items():
if brand in text_lower:
if not any(d == real_domain for d in domains_found):
for d in domains_found:
if looks_like_lookalike(d, real_domain):
findings.append(f'"{d}" looks like a lookalike of "{real_domain}".')
return findings
Run it with:
python3 phishing_checker.py
Paste in an email, then press Ctrl+D (or Ctrl+Z then Enter on Windows) to run the check.
What broke
My first lookalike check just compared string length, so "paypal.com" and "paypaI.com" (capital i instead of lowercase L) passed as identical because Python treated the character difference as irrelevant to length. I had to actually compare character by character and count how many differ, rather than relying on length alone.
I also had an early version that required pressing Enter twice to finish typing, which worked fine typed by hand but broke whenever I tried to pipe in a test email automatically. Reading everything at once until the input ends fixed it, and made pasting a whole email simpler too.
What I learned
Most phishing checks aren’t really about catching clever cryptography, they’re about catching small, boring inconsistencies: a brand name next to a domain that isn’t quite right. The hard part isn’t the code, it’s deciding what counts as “close enough” to flag without flagging real emails by mistake.
Try it yourself
Add more brands to the KNOWN_BRANDS list, or add a check for emails that use urgency words in the subject line specifically, since that’s an even stronger signal than urgency buried in the body.
The full code is on GitHub.
