Python 3 and a terminal. No email account is touched and nothing is sent.
macOS and Linux already include Python; on Windows install it from python.org with
“Add python.exe to PATH” ticked, then confirm with
python3 --version.
You will take a phishing email apart, not read about one. Everything below runs
on two small text files you create yourself, entirely offline. If you would rather not create
files, you can still read the outputs — but the step that changes how people read email is
step 3, and it lands much harder when the word pass appears on your own screen.
What Is Phishing?
Phishing is a type of cyberattack where an attacker impersonates a trusted entity to trick you into revealing sensitive information such as passwords, credit card numbers, or personal data. The name comes from the idea of "fishing" for victims -- casting out bait and waiting for someone to bite.
Phishing remains one of the most successful attack vectors because it targets human psychology rather than technical vulnerabilities. Phishing is consistently among the most common ways attackers gain an initial foothold. No amount of firewalls or antivirus software can protect you if you willingly hand over your credentials to an attacker.
Phishing exploits trust, urgency, and fear. Attackers craft messages that look legitimate and create scenarios where you feel pressured to act quickly -- before you have time to think critically about what you are doing.
Types of Phishing Attacks
Phishing comes in many forms, each tailored to different targets and communication channels. Understanding the varieties helps you recognize them regardless of how they arrive.
Email Phishing
The most common form. Attackers send mass emails that appear to come from legitimate organizations such as banks, online services, or government agencies. These emails typically contain a link to a fake website designed to harvest your credentials.
From: security@paypa1.com <-- Note: "paypa1" with number 1, not "paypal"
Subject: Your account has been limited
Dear Customer,
We have detected unusual activity on your account. Please verify
your identity immediately by clicking the link below or your
account will be permanently suspended within 24 hours.
[Verify Now] <-- Links to a fake website
Spear Phishing
Unlike mass email phishing, spear phishing targets a specific individual. The attacker researches the victim using social media, company websites, and public records to craft a highly personalized message. Because the email references real details about the victim's life or work, it is much more convincing.
Whaling
A form of spear phishing that targets high-value individuals such as CEOs, CFOs, and other senior executives. Whaling emails often impersonate business partners, board members, or legal authorities and involve large financial transactions or sensitive corporate data.
Smishing (SMS Phishing)
Phishing delivered via text message. Attackers send SMS messages claiming to be from your bank, a delivery service, or a government agency. The message usually contains a shortened URL that leads to a credential harvesting page.
Vishing (Voice Phishing)
Phishing conducted over the phone. An attacker calls pretending to be tech support, a bank representative, or a government official. They use urgency and authority to pressure you into providing personal information or granting remote access to your computer.
Attackers can make their phone number appear as any number they choose, including your bank's real number. Never trust a call solely because the caller ID looks legitimate. Hang up and call the organization back using the number on their official website.
Red Flags to Watch For
While phishing attacks are becoming more sophisticated, most still contain telltale signs that something is wrong. Train yourself to look for these indicators before taking any action.
- Urgency or threats -- "Your account will be closed in 24 hours" or "Immediate action required"
- Generic greetings -- "Dear Customer" or "Dear User" instead of your actual name
- Suspicious sender address -- The display name says "PayPal" but the email is from
support@paypa1-secure.com - Spelling and grammar errors -- Legitimate organizations proofread their communications
- Mismatched URLs -- The link text says one thing but the actual URL goes somewhere else
- Unexpected attachments -- Especially executable files (.exe, .scr) or Office documents with macros
- Requests for sensitive information -- Legitimate companies never ask for passwords via email
- Too good to be true -- Prize winnings, unexpected refunds, or free gifts from unknown sources
Examining Email Headers
Email headers contain technical metadata that reveals the true origin of a message. While the "From" field can be easily spoofed, the headers tell a more complete story. Learning to read them is a powerful skill for identifying phishing.
How to View Headers
Most email clients let you view the full message headers. In Gmail, click the three dots next to the reply button and select "Show original." In Outlook, open the message properties. In Thunderbird, go to View, then Message Source.
What to Look For
Return-Path: <bounce@suspicious-domain.xyz> <-- Does not match the "From" address
Received: from mail.suspicious-domain.xyz
(unknown [185.234.xx.xx]) <-- Unfamiliar sending server
Authentication-Results:
spf=fail <-- SPF check failed
dkim=none <-- No DKIM signature
dmarc=fail <-- DMARC check failed
These are email authentication protocols. SPF verifies the sending server is authorized, DKIM provides a cryptographic signature, and DMARC ties them together with a policy. If any of these show "fail" or "none," the email may not be from who it claims to be.
Checking URLs Before Clicking
The most dangerous part of a phishing email is usually the link. Before clicking any link in an email or message, take a moment to verify where it actually goes.
Hover Before You Click
On a desktop, hover your mouse over the link without clicking. Your email client or browser will show the actual URL in the bottom-left corner of the window or in a tooltip. Compare the displayed URL with what the link text claims.
Common URL Tricks
- Lookalike domains --
paypa1.com(number 1 instead of letter l),g00gle.com(zeros instead of o's) - Subdomain tricks --
paypal.com.attacker-site.com-- the real domain isattacker-site.com - URL shorteners --
bit.ly/xyz123hides the true destination - Encoded characters --
%70%61%79in the URL to obscure the domain name - Homograph attacks -- Using characters from other alphabets that look identical to Latin letters (e.g., Cyrillic "a" instead of Latin "a")
If an email asks you to log in to your bank or any other service, do not click the link. Open your browser, type the website address manually, and log in from there. This eliminates the risk of being redirected to a phishing page entirely.
What to Do If You Clicked a Phishing Link
If you realize you have clicked a phishing link or entered your credentials on a suspicious site, act quickly. The faster you respond, the more damage you can prevent.
- Change your password immediately -- Go directly to the real website and change the password for the affected account
- Enable two-factor authentication -- If not already enabled, set it up now to prevent unauthorized access even with stolen credentials
- Check for unauthorized activity -- Review recent login history, transactions, and account changes
- Scan your device -- Run a full antivirus scan in case the phishing site delivered malware
- Monitor your accounts -- Watch for unusual activity over the following weeks
- Change passwords on other accounts -- If you reused the same password elsewhere, change those too
Attackers often do not use stolen credentials immediately. If you change your password within minutes of entering it on a phishing site, you may prevent the attacker from ever accessing your account.
Reporting Phishing
Reporting phishing helps protect others and helps organizations take down fraudulent sites. Every report contributes to the collective defense against these attacks.
- Report to your email provider -- Most providers have a "Report phishing" button that helps train their spam filters
- Report to the impersonated organization -- Forward the phishing email to the real company (many have a dedicated address like
phishing@company.com) - Report to national authorities -- In the US, forward to the Anti-Phishing Working Group at
reportphishing@apwg.orgor file with the FTC - Report to your IT department -- If it happened at work, notify your security team immediately so they can warn others
- Report the phishing URL -- Use Google Safe Browsing or PhishTank to report the malicious URL
Read a Phishing Email the Way a Mail Server Does, in Five Steps
Every piece of advice about phishing tells you to “check the sender”. Almost nobody explains that the sender is written in three different places, that they routinely disagree, and that your mail app shows you the one an attacker fully controls. In the next twenty minutes you will open a real phishing message as raw text, pull those three fields apart with fifteen lines of Python, and finish with the most uncomfortable finding in the whole subject: this message passes SPF, DKIM and DMARC, all three, and is still a fraud. Every line of output below was produced by running these files.
Go: open a terminal in a folder you can write to — cd ~/Desktop on macOS or Linux, cd %USERPROFILE%\Desktop on Windows.
Do: save this as phish.eml. It is a phishing message reproduced in the format
mail actually travels in: a block of headers, a blank line, then the body. Your mail app shows
you roughly four of these lines and hides the rest.
Return-Path: <billing@secure-mail-247.co>
Received: from mail.finkatana.com (localhost [127.0.0.1])
by mail.finkatana.com with ESMTPS id 4Wq8Hn2Kx1z
for <you@example.com>; Fri, 22 Aug 2026 09:14:02 +0000
Received: from vps-31-88.hostwind.net (vps-31-88.hostwind.net [185.244.31.88])
by mail.finkatana.com with ESMTP id 4Wq8Hn2Kx0y
for <you@example.com>; Fri, 22 Aug 2026 09:14:01 +0000
Authentication-Results: mail.finkatana.com;
spf=pass (sender IP is 185.244.31.88) smtp.mailfrom=secure-mail-247.co;
dkim=pass header.d=secure-mail-247.co;
dmarc=pass header.from=secure-mail-247.co
From: "Microsoft 365 Billing" <billing@secure-mail-247.co>
Reply-To: recovery.desk@mail-verify.support
To: you@example.com
Subject: Action required: your Microsoft 365 subscription will be cancelled
Date: Fri, 22 Aug 2026 09:14:00 +0000
Message-ID: <20260822091400.4a91@secure-mail-247.co>
Content-Type: text/plain; charset=utf-8
Your payment method was declined. Confirm your billing details within 24 hours
or your account will be closed. Confirm here:
https://login.microsoftonline.com.account-verify.secure-mail-247.co/session
You should see: nothing yet — this is a data file. Confirm it saved with wc -l phish.eml
(Windows PowerShell: (Get-Content phish.eml).Count), which should report:
22 phish.eml
Read the From: line and the last line of the body, then stop. That is
approximately what a mail client displays, and on that evidence the message is indistinguishable
from a real one.
If not: a count near 22 is fine — some editors add a trailing newline. A count of
1 means the file saved as one long line, which breaks the header format; re-copy it
and check that your editor is not set to wrap or reformat. If wc is not found on
Windows, use the PowerShell form above.
Go: the same folder.
Do: save this as who.py and run python3 who.py phish.eml. It uses
Python's own email module — the same parsing rules your mail server uses, not a
guess.
import email, email.utils, sys
msg = email.message_from_file(open(sys.argv[1], encoding="utf-8"))
def addr(header):
raw = msg.get(header)
if not raw:
return None, None
name, mail = email.utils.parseaddr(raw)
return name, mail
def domain(mail):
return mail.split("@")[-1].lower() if mail and "@" in mail else ""
shown_name, shown_mail = addr("From")
_, envelope = addr("Return-Path")
_, replyto = addr("Reply-To")
print("the name you see :", shown_name)
print("the address it hides:", shown_mail)
print("who actually sent it:", envelope)
print("where replies go :", replyto or "(same as From)")
print()
print("From domain :", domain(shown_mail))
print("Return-Path domain:", domain(envelope))
print("Reply-To domain :", domain(replyto))
print()
if replyto and domain(replyto) != domain(shown_mail):
print("MISMATCH: replies leave the sender's own domain")
if shown_name and "." in shown_name.replace(" ", ""):
print("NOTE: the display name contains a domain-like string")
You should see: three names for one sender, and they do not agree:
the name you see : Microsoft 365 Billing
the address it hides: billing@secure-mail-247.co
who actually sent it: billing@secure-mail-247.co
where replies go : recovery.desk@mail-verify.support
From domain : secure-mail-247.co
Return-Path domain: secure-mail-247.co
Reply-To domain : mail-verify.support
MISMATCH: replies leave the sender's own domain
The display name is free text. Anyone may set it to
“Microsoft 365 Billing”, or to your bank's name, or to your own boss's name; it is
checked by nothing, and on a phone it is often the only part shown. The
Reply-To is the second lever: your reply goes to a third domain, so the
conversation moves somewhere the attacker reads even if the first address is later shut
down.
If not: FileNotFoundError means the two files are in different folders —
run ls (Windows: dir) and confirm both names appear. If every field
prints None, the blank line between the headers and the body is missing, so the
parser treated the whole file as a body.
Go: the same folder. This is the step that changes how you read email.
Do: save this as auth.py and run python3 auth.py phish.eml. The
Authentication-Results header is written by your mail server after it has
done the cryptography, so it is not something the sender can forge.
import email, re, sys
msg = email.message_from_file(open(sys.argv[1], encoding="utf-8"))
results = msg.get("Authentication-Results", "")
for check in ("spf", "dkim", "dmarc"):
m = re.search(check + r"=(\w+)", results)
print("%-6s %s" % (check, m.group(1) if m else "absent"))
print()
mailfrom = re.search(r"smtp\.mailfrom=([^\s;]+)", results)
headerfrom = re.search(r"header\.from=([^\s;]+)", results)
print("all three checks describe this domain:", mailfrom.group(1) if mailfrom else "?")
print("and DMARC aligned it with :", headerfrom.group(1) if headerfrom else "?")
print()
print("They agree. The mail is genuinely from the domain it claims.")
print("The question they never answer: is that domain the one you meant to trust?")
You should see: a clean sweep — on a message that is unambiguously a fraud:
spf pass
dkim pass
dmarc pass
all three checks describe this domain: secure-mail-247.co
and DMARC aligned it with : secure-mail-247.co
They agree. The mail is genuinely from the domain it claims.
The question they never answer: is that domain the one you meant to trust?
This is the single most misunderstood fact about email security.
SPF asks “is this server allowed to send for this domain?”, DKIM asks
“was this message signed by this domain?”, and DMARC asks “do those two
domains match the visible From?”. Every one of them is a question about
self-consistency. The attacker registered secure-mail-247.co, published his
own SPF record and his own DKIM key, and so answers all three truthfully. A green
padlock-shaped verdict here means the mail is authentically from whoever sent it — never
that whoever sent it is who you think.
If not: if any check prints absent, the Authentication-Results
header was lost when copying — it is the four-line block near the top and its continuation
lines must stay indented. Real mail from a provider that does no checking also shows
absent, which is itself worth knowing.
Go: the same folder.
Do: save this as hops.py and run python3 hops.py phish.eml. Each
server that touches a message adds a Received line to the top, so the list
is in reverse order — the oldest hop is last.
import email, sys
msg = email.message_from_file(open(sys.argv[1], encoding="utf-8"))
hops = msg.get_all("Received") or []
print("%d Received header(s). The LAST one is the FIRST hop." % len(hops))
print()
for n, h in enumerate(reversed(hops), 1):
first_line = " ".join(h.split())
print("hop %d: %s" % (n, first_line[:78]))
print()
print("Everything above hop 1 is your own provider handling the mail.")
print("Hop 1 is where it entered the internet -- that is the real sender.")
You should see: the message entering the internet from a rented virtual server, not from Microsoft:
2 Received header(s). The LAST one is the FIRST hop.
hop 1: from vps-31-88.hostwind.net (vps-31-88.hostwind.net [185.244.31.88]) by mail.f
hop 2: from mail.finkatana.com (localhost [127.0.0.1]) by mail.finkatana.com with ESM
Everything above hop 1 is your own provider handling the mail.
Hop 1 is where it entered the internet -- that is the real sender.
Hops added by your own provider are trustworthy, because your provider wrote them. Hops claimed below that point are just text the sender supplied and can be invented freely — which is why the only one worth reading is the first one your provider vouched for.
If not: if it reports 0 Received header(s), the continuation lines (the ones
starting with spaces) were flattened when copying; a Received header is several lines
and every line after the first must begin with whitespace.
Go: the same folder. A detector you have never seen produce a negative is not a detector.
Do: save this as real.eml — a legitimate Microsoft account notice —
then run python3 who.py real.eml and python3 auth.py real.eml.
Return-Path: <bounce-427193@bounces.account.microsoft.com>
Received: from mail.finkatana.com (localhost [127.0.0.1])
by mail.finkatana.com with ESMTPS id 4Wq9Jp3Lz2a
for <you@example.com>; Fri, 22 Aug 2026 10:02:11 +0000
Received: from mail-db8eur05on2131.outbound.protection.outlook.com (mail-db8eur05on2131.outbound.protection.outlook.com [40.107.20.131])
by mail.finkatana.com with ESMTPS id 4Wq9Jp3Lz1b
for <you@example.com>; Fri, 22 Aug 2026 10:02:10 +0000
Authentication-Results: mail.finkatana.com;
spf=pass (sender IP is 40.107.20.131) smtp.mailfrom=bounces.account.microsoft.com;
dkim=pass header.d=account.microsoft.com;
dmarc=pass header.from=account.microsoft.com
From: "Microsoft account team" <account-security-noreply@account.microsoft.com>
To: you@example.com
Subject: Microsoft account security info was added
Date: Fri, 22 Aug 2026 10:02:09 +0000
Message-ID: <20260822100209.7f22@account.microsoft.com>
Content-Type: text/plain; charset=utf-8
Security info was added to your Microsoft account. If this was you, no action
is needed. Review your security info at https://account.microsoft.com/security
You should see: a message that passes exactly the same three checks the phishing one passed:
the name you see : Microsoft account team
the address it hides: account-security-noreply@account.microsoft.com
who actually sent it: bounce-427193@bounces.account.microsoft.com
where replies go : (same as From)
From domain : account.microsoft.com
Return-Path domain: bounces.account.microsoft.com
Reply-To domain :
Note what is not a difference. The genuine message's
Return-Path also differs from its From — that is normal, because
bulk senders route bounces to a separate address. Chasing “Return-Path mismatch” as a
rule would flag this real message and clear plenty of fake ones.
Three things actually separate them. The genuine bounce domain is a
subdomain of the same organisation (bounces.account.microsoft.com sits under
microsoft.com); there is no Reply-To pointing somewhere unrelated; and
the domain in the From is one you can reach independently by typing it yourself. The
first two your script can check. The third it cannot — and that is the judgement the
headers will never make for you.
If not: if Reply-To domain prints a blank line rather than being absent, that is
correct — the field is missing, so domain() returns an empty string. If
auth.py reports dmarc pass aligned to
account.microsoft.com while SPF describes the bounces subdomain, that is also correct
and is exactly how DMARC alignment is designed to work.
Without scrolling up: an email arrives from your company's payroll provider, your mail
app shows no warning banner, and you check the raw headers and find spf=pass,
dkim=pass and dmarc=pass. Your colleague says that settles it. What is
wrong with that reasoning, and what would you check instead? Answer: all three checks are questions the sending domain answers about itself. They prove the
message really came from the domain in the From line and was not altered in transit
— nothing more. An attacker who registers a domain and publishes his own SPF record and DKIM
key passes all three on his first attempt, which is exactly what step 3 demonstrated. What to
check instead is the domain itself: read it right-to-left to find the registrable name, ask
whether it is the payroll provider's actual domain rather than a plausible variation of it, and
confirm the request through a channel you chose — a number from a previous invoice or your
own records, never one supplied by the message.
Now do it without the page: take a real message from your own inbox — most mail apps offer “show
original”, “view source” or “show raw message” — save it as
mine.eml and run all three scripts on it. Then extend who.py so it
reports the registrable domain (the last two labels, e.g.
microsoft.com from bounces.account.microsoft.com) and flags the case
that matters: a Reply-To whose registrable domain differs from the
From's. You will find that rule catches redirect-to-attacker mail while leaving
ordinary bulk senders alone.
Summary
Phishing is the most common and often the most effective cyberattack because it targets people rather than technology. Defending yourself requires awareness and healthy skepticism.
- Phishing uses impersonation to trick you into revealing sensitive information
- It comes in many forms -- email, SMS, phone calls, and targeted attacks against specific individuals
- Red flags include urgency, generic greetings, mismatched URLs, and requests for sensitive data
- Email headers reveal the true origin of messages and whether authentication checks passed
- Always verify URLs by hovering before clicking, and navigate manually when in doubt
- If you fall for phishing, change passwords immediately, enable 2FA, and monitor your accounts
- Report phishing to your email provider, the impersonated organization, and relevant authorities
The best defense against phishing is a habit of pausing before acting on any unexpected message. Take five seconds to verify the sender, check the URL, and ask yourself whether the request makes sense. That brief pause can save you from a major security incident.