Interactive attack sandbox
Toggle between the vulnerable and hardened version of a login form, then try an attack. Everything runs locally in your browser — no real database, no real requests — so it is completely safe. This mirrors the "introduce then fix" workflow at the heart of the project.
Mock login
Try one of these payloads in the password box:
' OR '1'='1
SQLi: '; DROP TABLE users;--
XSS: <script>…</script>
Normal: hunter2
What the server does
Result
Why this matters: in the vulnerable build the password is glued straight into a SQL string, so ' OR '1'='1 makes the query always true and logs you in as admin. The hardened build uses a parameterised query and treats your input as data, never code — so the same payload just fails as a wrong password.
Overview
The goal is to experience both sides of security: you build a functioning web application, then act as an attacker against your own code, then act as a defender who fixes it. That loop — build, break, harden — is exactly how professional secure-development (DevSecOps) teams work.
A good target app is small but realistic: a login system, user posts/resources, comments, and file uploads. Each feature is a natural home for a specific vulnerability class.
Key features
- Backend in Flask or Django (Python).
- Vulnerabilities introduced then fixed: SQL injection, XSS, CSRF, and more.
- Full penetration test with OWASP ZAP plus manual testing.
- Security best practices: HTTPS, secure auth, input validation, output encoding.
OWASP Top 10 — your checklist of what to attack & defend
The OWASP Top 10 is the industry-standard list of the most critical web-app risks. Aim to demonstrate at least three or four of these in your app.
Users reaching pages/data they shouldn't. Fix: server-side authorization checks on every request.
Passwords or data stored/sent in the clear. Fix: HTTPS + hash passwords with bcrypt/argon2.
Untrusted input runs as a query/command. Fix: parameterised queries / ORM.
Missing security thinking up front. Fix: threat-model before you build.
Debug mode on, default creds, verbose errors. Fix: harden configs, disable debug in prod.
Weak sessions, no lockout, guessable passwords. Fix: secure sessions, rate-limit, MFA.
Attacker's script runs in a victim's browser. Fix: escape/encode all output, use CSP.
A malicious site triggers actions as the logged-in user. Fix: per-session CSRF tokens.
Build plan — step by step
vulnerable): raw SQL string concatenation, unescaped output, no CSRF token, debug mode on.secure): parameterised queries/ORM, output encoding + Content-Security-Policy, CSRF tokens, bcrypt password hashing, HTTPS, input validation, disable debug.Penetration testing with OWASP ZAP + manual methods
Automated (OWASP ZAP)
- Run ZAP as a proxy in front of your browser to spider the app and map every page.
- Use the Automated Scan for a first pass of common issues.
- Review the Alerts pane; triage false positives.
- Export the HTML report — great evidence for your write-up.
Manual methods
- Craft SQLi payloads by hand (like the sandbox above) to confirm ZAP's findings.
- Test stored XSS by posting
<script>in a comment and seeing if it executes for other users. - Build a tiny external HTML page that auto-submits a form to your app to prove CSRF.
- Check access control by editing URLs/IDs to reach another user's data.
localhost or a private VM. Using ZAP or these techniques against websites you don't own is illegal.Hardening checklist & example fixes
| Vulnerability | Vulnerable code | Hardened code |
|---|---|---|
| SQL Injection | f"SELECT * FROM users WHERE name='{u}'" |
cur.execute("SELECT * FROM users WHERE name=?", (u,)) |
| XSS | Rendering {{ comment|safe }} |
Default auto-escaping {{ comment }} + a Content-Security-Policy header |
| CSRF | Form with no token | Flask-WTF / Django CSRF token in every state-changing form |
| Password storage | Plain text in DB | bcrypt.hashpw(...) / Django's make_password |
| Transport | HTTP | HTTPS (TLS), Secure + HttpOnly cookies |
Learning outcomes
Writing code that resists the most common attacks by default.
Recognising and mitigating the highest-impact web risks.
Thinking like an attacker before writing code.
Baking security testing into the build cycle.
Documenting findings, impact, and fixes clearly.
OWASP ZAP, git branches, ORMs, TLS.