Skip to content
QYChan Qing Yee
CV
← all projects

SecureExam UTM

DIGITEX 2026 Silver

Solo full-stack · FYP under Prof. Madya Ts. Dr. Siti Hajar Othman

A production examination platform where every request is treated as untrusted: eight defense-in-depth layers spanning TLS 1.3, an Nginx/UFW/fail2ban edge, per-request IP binding, JWT stage-machine MFA, and least-privilege RBAC — 26 mapped controls over 35+ endpoints, built solo and operated on a hardened DigitalOcean Singapore host.

11,700+
LOC
26
Controls
35+
Endpoints
4
RBAC roles
NginxTLS 1.3Node / ExpressMySQL 8Flask· deployed on DigitalOcean Singapore

// the threat model

What SecureExam defends against

An online exam platform is a high-value target with two distinct adversaries: students motivated to cheat, and attackers motivated by the credentials and exam data behind the login. SecureExam treats every request as untrusted and defends in depth — no single control is load-bearing.

Exam integrity

Candidates attempting to cheat — leaving the exam window, copy/paste, dev tools, or sharing a live session.

Account takeover

Credential stuffing and password reuse against the login, and hijacking of stale or stolen sessions.

Data theft

Exfiltration of exam content, answers, or personal data through the app, the database, or the transport.

Tampering

Privilege escalation across roles, broken access control (IDOR), and injection against the API and database.

// zero-trust architecture

Trace a request through eight defense-in-depth layers

Select a layer to see the control it enforces and the threats it stops. Each layer is linkable directly.

▼ request in
▼ trusted action

layer 01 · client

Browser lockdown

Fullscreen lockdown, copy/paste + devtools restrictions, heartbeat tokens rotating through the session.

threats stopped

  • Exam cheating
  • Stale-session hijack

Zero-trust layers, in request order

  1. Layer 01, Browser lockdown (client): Fullscreen lockdown, copy/paste + devtools restrictions, heartbeat tokens rotating through the session. Threats stopped: Exam cheating, Stale-session hijack.
  2. Layer 02, TLS 1.3 (transport): End-to-end HTTPS (Let’s Encrypt) + HSTS. Threats stopped: Eavesdropping, Man-in-the-middle.
  3. Layer 03, Nginx reverse proxy (edge): App fronted behind UFW firewall, fail2ban, rate limiting; Node never publicly exposed. Threats stopped: Brute force, Port scanning, Direct app exposure.
  4. Layer 04, JWT + TOTP MFA (authn): Short-lived JWTs + TOTP multi-factor auth. Threats stopped: Credential stuffing, Password reuse.
  5. Layer 05, RBAC · 4 roles (authz): Least privilege across 4 roles and 35+ endpoints. Threats stopped: Privilege escalation, Broken access control (IDOR).
  6. Layer 06, Node + Express (app): Input validation + secure headers across 25+ mapped controls. Threats stopped: XSS, CSRF, Injection.
  7. Layer 07, MySQL 8 (data): Least-privilege DB user, parameterized queries, hardened auth. Threats stopped: SQL injection, Data exfiltration.
  8. Layer 08, Risk scorer (detection): Flask microservice (Control #26), Isolation Forest anomaly model, bound to localhost (127.0.0.1:8001). Threats stopped: Anomalous exam behaviour.

// deployment & hardening

I didn’t just build SecureExam — I shipped and operated it. The platform runs on a hardened DigitalOcean Singapore droplet:

  • Nginx reverse proxy fronts the app — Node is never publicly exposed.
  • UFW firewall + fail2ban + rate limiting at the edge.
  • Let’s Encrypt TLS 1.3 with HSTS.
  • PM2 runs two processes: secureexam-backend (Node) and risk-scorer (Flask) bound to 127.0.0.1:8001.
  • Least-privilege MySQL user; parameterized queries throughout.
  • SSH hardening (key-only auth, no root login).

// what I’d do next

  • Move secrets out of env files into a managed vault.
  • Add automated dependency and container scanning to CI.
  • Train the risk scorer on labelled data and track precision/recall, not just anomaly scores.
  • Add an automated IDOR/RBAC test suite over all 35+ endpoints.

Want to walk through any layer in an interview?