Skip to content

Repository files navigation

SecureLearn — Web Application Security Training Platform

SecureLearn

An educational web application security training platform built around the OWASP Top 10.
Learn to write secure code by studying vulnerable and secure implementations side by side.

Python Flask Tests Coverage License Docker PostgreSQL Security

CI


What is this?

SecureLearn is a hands-on learning platform that teaches web application security. Each topic isolates one vulnerability class and walks through how it arises, how it's exploited, and — most importantly — how to prevent it, with a signature vulnerable-vs-secure code comparison, an attack-flow diagram, a real-world case study, a quiz, and practice exercises.

⚠️ This is a teaching tool, not a target. The vulnerable snippets are inert illustrations shown for study. SecureLearn ships no runnable exploitable endpoints, and the application itself is built to model the secure practices it teaches.

Highlights

  • 📚 12 in-depth modules covering the OWASP Top 10 vulnerability classes
  • 🔴🟢 Vulnerable-vs-secure diptych — the core teaching device, on every module
  • 🧭 Attack-flow diagrams rendered with Mermaid
  • 🏛️ Real-world case studies (Heartland, the Samy worm, Capital One, LinkedIn, Firesheep, …)
  • 📝 48 quiz questions with per-question explanations and scoring
  • 📊 Progress tracking — completion ring, per-module status, quiz stats, activity feed
  • 🔎 Ranked search across titles, tags, OWASP ids and body text
  • 🌗 Light / dark theme, responsive, keyboard-accessible, reduced-motion aware
  • 🔌 Read-only JSON API reusing the same content + service layer
  • 🐳 Docker & Docker Compose, ✅ 194 tests / 96% coverage, 🔁 GitHub Actions CI
  • 🗄️ Versioned schema — Flask-Migrate / Alembic, verified on SQLite and PostgreSQL
  • 🚦 Rate limiting on the credential-guessing surfaces (Flask-Limiter)
  • 📈 Ops-ready — liveness/readiness probes, JSON logs with request ids, optional Prometheus metrics

The platform practises what it teaches

Control Implementation
Parameterised queries SQLAlchemy ORM everywhere
Salted password hashing User.set_password (Werkzeug)
CSRF protection on all forms Flask-WTF CSRFProtect
State changes never on GET POST /logout, CSRF-protected like every other mutation
Deny-by-default authorization security.role_required
Security headers + strict CSP security.apply_security_headers
HSTS on HTTPS responses only security.apply_security_headers (never advertised over plain http)
Output escaping Jinja2 autoescape on all user content
Open-redirect defence auth._safe_next
Session regeneration on login session.clear() in auth.login
Subresource Integrity on CDN assets integrity + crossorigin on every third-party script/style
Fail-fast on insecure config _validate_config refuses a known SECRET_KEY in production
No published credentials in prod Seeding refuses the default admin password; demo account is off
Forged proxy headers untrusted X-Forwarded-* honoured only when TRUSTED_PROXY_COUNT is set
Nonce-based CSP (no unsafe-inline scripts) security.build_csp — a fresh 128-bit nonce per request
Brute-force resistance Flask-Limiter on login, registration, password change, search
Errors leak nothing Generic messages to the client; the detail goes to the log
Runs unprivileged in Docker USER appuser — never root

Quickstart

git clone https://github.com/Joshwa-jd/securelearn.git
cd securelearn

python -m venv .venv && source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -r requirements.txt

python run.py            # http://127.0.0.1:5000  (auto-creates & seeds the DB)

Demo accounts (development only): learner / Learner!2345 · admin admin / Admin!2345

These are seeded for local exploration and their passwords are published here, so production refuses them: the demo account is not created under FLASK_CONFIG=production, and seeding aborts while SEED_ADMIN_PASSWORD is still the default.

Run with Docker

Production config requires real secrets — the app fails fast rather than boot with a known key, so supply them via a .env file (compose reads it) or the shell:

cp .env.example .env
# set SECRET_KEY and SEED_ADMIN_PASSWORD to strong, unique values
#   python -c "import secrets; print(secrets.token_hex(32))"

docker compose up --build     # served by gunicorn on http://localhost:8000

Set WEB_PORT if 8000 is already taken. Behind a TLS-terminating reverse proxy, also set SESSION_COOKIE_SECURE=true and TRUSTED_PROXY_COUNT=1 so HTTPS is detected and HSTS is sent.

The container runs as an unprivileged user, applies migrations on start-up, and exposes /api/health/live and /api/health/ready for orchestrators. See the deployment guide for the full production path.

Modules

Module OWASP Level Time
SQL Injection A03:2021 Injection Beginner 25 min
Cross-Site Scripting (XSS) A03:2021 Injection Beginner 25 min
Output Encoding A03:2021 Injection Intermediate 20 min
Input Validation A03:2021 Injection Beginner 20 min
Cross-Site Request Forgery (CSRF) A01:2021 Broken Access Control Intermediate 20 min
Insecure Direct Object References (IDOR) A01:2021 Broken Access Control Intermediate 20 min
Authorization & Access Control A01:2021 Broken Access Control Intermediate 20 min
Authentication Security A07:2021 Identification & Authentication Failures Intermediate 25 min
Session Security A07:2021 Identification & Authentication Failures Intermediate 20 min
Server-Side Request Forgery (SSRF) A10:2021 SSRF Advanced 25 min
File Upload Security A04:2021 Insecure Design Advanced 25 min
Security Headers A05:2021 Security Misconfiguration Beginner 20 min

Every module page includes: Overview · How it works · Attack flow · Vulnerable vs secure code · Common mistakes · Prevention · OWASP mapping · Case study · Exercises · Quiz · References.

Screenshots

SecureLearn landing page: the vulnerable-vs-secure code comparison and the module catalogue

A module page in dark mode: attack-flow diagram, vulnerable-vs-secure code panes, prevention guidance and the knowledge check
A module page (dark theme) — attack flow, the vulnerable-vs-secure diptych, OWASP mapping, case study and quiz.

Learner dashboard showing the completion ring, per-module progress and recent activity
The learner dashboard — completion ring, per-module status and activity feed.

These are captured from a running instance so they always match the code. To regenerate them (light and dark, every page), see docs/screenshots/ for the Playwright script.

Tech stack

Backend: Python 3.11–3.13, Flask 3, Flask-SQLAlchemy, Flask-Login, Flask-WTF, Flask-Migrate, Flask-Limiter, SQLAlchemy 2, gunicorn Frontend: Jinja2, Bootstrap 5.3, Bootstrap Icons, Mermaid, a bespoke CSS token layer, vanilla JS (theme toggle) Data: SQLite by default, PostgreSQL for production (any SQLAlchemy URL via DATABASE_URL); schema versioned with Alembic Ops: structured JSON logging, liveness/readiness probes, optional Prometheus metrics Tooling: pytest, pytest-cov, flake8, Bandit, pip-audit, Docker, Docker Compose, GitHub Actions

Project structure

securelearn/
├── app/
│   ├── __init__.py        # application factory
│   ├── config.py          # environment-driven config
│   ├── models.py          # SQLAlchemy models
│   ├── forms.py           # WTForms
│   ├── security.py        # role_required · headers · CSP nonce · markdown
│   ├── services.py        # progress & dashboard logic
│   ├── seed.py            # migrate + default users (idempotent)
│   ├── logging_config.py  # structured logging + request correlation ids
│   ├── monitoring.py      # health probes + Prometheus metrics
│   ├── extensions.py      # db · login · csrf · migrate · limiter singletons
│   ├── blueprints/        # main · auth · learning · api
│   └── content/           # schema + registry + 12 module files (typed data)
├── migrations/            # Alembic environment + versioned revisions
├── templates/             # base · partials · pages · errors
├── static/                # css/style.css · js/main.js
├── tests/                 # pytest suite (194 tests)
├── scripts/smoke_test.py  # black-box check of a running instance (used by CI)
├── docker/Dockerfile
├── docker-compose.yml
├── .github/workflows/ci.yml
├── run.py                 # dev entrypoint
└── wsgi.py                # gunicorn entrypoint

Testing & quality

pip install -r requirements-dev.txt
pytest                     # 194 tests
pytest --cov=app           # coverage (96%)
flake8                     # lint — scope comes from setup.cfg
bandit -q -r app scripts   # static analysis
pip-audit -r requirements.txt   # dependency advisories

Every push and pull request runs, in parallel:

Job What it proves
Lint flake8 is clean
Test (3.11 · 3.12 · 3.13) 194 tests pass; coverage stays ≥ 90%
Test (PostgreSQL) the same suite passes on the production engine
Migrations (SQLite · PostgreSQL) upgrade → flask db check → downgrade → upgrade again
Security scan Bandit + pip-audit
Docker the image builds, boots, serves, and runs as non-root
Compose the documented docker compose up path actually works

The Docker and Compose jobs smoke-test a running container over HTTP — health, readiness, liveness, the API contract, security headers and per-request CSP nonces — parsing JSON rather than grepping it, because production serves compact JSON and development does not.

Documentation

Doc Contents
Installation Local + Docker setup, configuration
Deployment Production: secrets, PostgreSQL, migrations, probes, proxy, rollback
Architecture Layers, request lifecycle, data model, diagrams
API Reference JSON API endpoints and examples
Developer Guide Project layout, adding modules/routes, migrations
User Guide How to use the platform
Future Improvements Roadmap ideas
Contributing How to contribute
Security Policy Reporting a vulnerability, threat model, scope
Changelog Release history

Design notes

The content is authored as typed Python dataclasses rather than scattered across templates. This makes every module unit-testable, lets a single template render all of them consistently, and allows search to index every field. Adding a module is a single new file — no route or template changes required.

Roadmap

v1.0.0 is feature-complete. Ideas under consideration, roughly in order:

  • More modules — XXE, insecure deserialization, SSTI, dependency/supply-chain security, cryptographic failures, logging & monitoring failures
  • Learning paths — curated sequences (e.g. "Injection track") with prerequisites
  • Spaced repetition — resurface quiz questions a learner previously got wrong
  • Admin authoring UI — manage content without editing Python files
  • i18n — externalise strings and translate the module catalogue
  • Redis-backed rate limiting by default for multi-worker deployments

See docs/future-improvements.md for the fuller list, including the trade-offs behind each. Issues and proposals are welcome.

Contributing

Contributions are welcome — bug reports, new modules, docs fixes and accessibility improvements especially.

pip install -r requirements-dev.txt
pytest && flake8 && bandit -q -r app scripts

Please read CONTRIBUTING.md for the workflow (branching, migrations, adding a module) and the Code of Conduct. Security issues should not be filed as public issues — follow SECURITY.md instead.

License

MIT © 2026 Joshwa J D


Built as a portfolio project to demonstrate secure application development and the OWASP Top 10. For learning and authorised testing only.

About

Secure OWASP Top 10 learning platform built with Flask, featuring interactive security modules, quizzes, progress tracking, authentication, security hardening, PostgreSQL, Docker, and CI/CD.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages