A small Flask app that teaches people how a credential-phishing page works — and how to defend against one. It shows a convincing fake login page, then the instant someone "logs in," flips to a lesson that reveals it was a simulated phish, points out the red flags they missed, and lists the defenses that would have protected them.
Built for security-awareness training, student learning, and as a portfolio piece.
This project is intentionally non-operational and safe to run and host:
- It never contacts Instagram or any real service.
- It never stores, logs, transmits, or echoes any password — the password field is discarded the instant the form is received. This is enforced by automated tests (see
tests/).- It shows a visible "simulation" banner, so no real person is deceived.
- The only thing recorded is an anonymous counter of how many times the demo ran — no usernames, no passwords, no personal data of any kind.
Use it only for education and awareness training. Cloning a real brand's login page to capture live credentials is illegal in most countries — this repo deliberately does not do that, and you shouldn't either.
- How phishing actually works — the lure → clone → capture → takeover chain.
- The reliable tells that separate a fake login page from the real thing.
- The defenses that stop credential phishing even when someone gets fooled.
- On the engineering side: a small, tested Flask app with CI and Docker.
| The bait — a look-alike login page | The lesson — shown the moment you "log in" |
|---|---|
![]() |
![]() |
- Lure — a victim gets a message ("suspicious login, verify your account") with a link to a look-alike page.
- Clone — the page (
templates/index.html) copies the real login screen closely enough to feel legitimate. - Capture — the victim types their username and password and submits.
- Replay / takeover — a real attacker would immediately use those credentials on the genuine site. This demo deliberately stops here — that step is the line between a lesson and a crime.
- Check the address bar. The real site lives at its own exact domain; a phish sits on a look-alike or unrelated one. This is the single most reliable tell.
- Trust your password manager's silence. It autofills by exact domain — if it won't offer to fill a saved site, you're probably not on the real one.
- Expect a real second factor. A page that "logs you in" from a password alone, with no 2FA prompt, is suspicious.
- Watch the details. Mixed languages, typos, and dead links signal a fake.
- Turn on two-factor authentication everywhere — a stolen password alone won't work.
- Use a password manager and let it be your domain check.
- Type addresses or use bookmarks instead of clicking links in messages.
- When in doubt, go to the site directly and verify there.
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
python main.pyOpen http://localhost:8080:
/— the simulated login page/stats— anonymous count of how many times the demo caught someone
docker build -t spot-the-phish .
docker run -p 8080:8080 spot-the-phishThe tests verify the core safety promise — that a submitted password is never stored or echoed back:
pip install -r requirements-dev.txt
pytest -v| Path | Purpose |
|---|---|
main.py |
Flask app: serves the page, shows the lesson on submit |
stats.py |
Anonymous, credential-free counter (SQLite) |
templates/index.html |
Simulated login page (with warning banner) |
templates/caught.html |
Teaching screen shown after a "capture" |
templates/stats.html |
Simple results page |
tests/ |
Automated tests, including the "password is never stored" guarantee |
Dockerfile |
Container build |
.github/workflows/ci.yml |
CI: runs the tests on every push/PR |
Python · Flask · Jinja2 · SQLite · pytest · Docker · GitHub Actions
MIT © 2026 Harshit Patel

