Waleed Bukhari

Open to SOC Analyst and blue-team roles

All work

FreightDesk, a secure business application

A hardened Flask logistics portal with live FX, RBAC and a full security self-assessment

Context
Coursework (CSEC2001D) · Secure web app
Year
2025/26
The OWASP Top 10 (2021), A01 to A10, one cell each. Lit: the 7 with an automated test. Dim: A06, covered by pinned dependencies, and A07 and A09, shown in code.

Problem

The brief’s real test isn’t “can you build a CRUD app” but “can you build one that withstands the common business-application attacks”: SQL injection, broken access control, CSRF, session hijacking and unsafe outbound requests. The emphasis is as much on how it’s secured as on what it does.

Built for the Secure Scripting & Business Applications brief (CSEC2001D). Staff sign in against role-based permissions, manage freight shipments in a normalised SQLite database, and see each shipment’s declared value converted to a base currency using exchange rates pulled live over HTTPS from a remote web service.

What I did

A Flask application factory wires configuration, the database, a remote FX client, authentication, central CSRF enforcement and security headers. Every query is parameterised; passwords are PBKDF2-hashed; access control is enforced server-side with role decorators; and the outbound FX call is HTTPS-only with a timeout, defensive parsing and an offline fallback.

Security-by-design controls

Each control is deliberately hand-rolled so the mechanism is visible and auditable, and each is backed by a test.

  • Injection: parameterised queries everywhere; allow-list input validation
  • Access control: server-side role decorators (viewer < clerk < admin), never UI-only
  • CSRF: per-session token enforced centrally for every state-changing request
  • Session: PBKDF2 hashes, HttpOnly + SameSite cookies, session rotation on login
  • Headers: strict CSP, X-Frame-Options DENY, nosniff, Referrer-Policy

Remote web service integration

Shipment values in mixed currencies are converted using live rates from open.er-api.com, a public FX API that needs no key. Rates are fetched over HTTPS with a 4-second timeout, parsed defensively, cached for an hour, and replaced by bundled fallback rates if the service is unreachable. The endpoint is fixed in config, not user-controlled, to resist SSRF.

Self-penetration test (OWASP)

I attacked my own app and documented it: SQL-injection auth-bypass and table-drop payloads (blocked by parameterisation), forced-browsing privilege escalation (403 server-side), CSRF replay (400), reflected XSS (Jinja autoescape + CSP) and clickjacking (frame-ancestors none). SECURITY.md maps each OWASP Top 10 (2021) category to its threat, control and evidence: 7 categories have a named test, A07 (authentication) and A09 (logging) point to the code that implements them, and A06 (vulnerable components) relies on a minimal pinned dependency set.

Evidence

Automated tests
17
auth · RBAC · CSRF · SQLi · FX
OWASP Top 10 (2021) categories with a test
7 of 10
A07 and A09 shown in code, A06 by pinned deps · SECURITY.md
RBAC roles
3
viewer · clerk · admin
String-built SQL
0
parameterised queries only · SQLi tests in the repo
  1. In
    • Browsersession + CSRF
  2. Core
    • Flask appRBAC · validation
    • SQLiteparameterised SQL
  3. Local AI
    • Remote FX APIHTTPS · cached · fallback
  4. Out
    • Audit logappend-only
Secure request pipeline, in the order data moves through it.

Stack and tools

  • Python · Flask 3
  • SQLite (parameterised SQL)
  • Werkzeug PBKDF2 auth
  • Role-Based Access Control
  • CSRF tokens · CSP · security headers
  • Remote FX web service (requests)
  • unittest (17 tests)

Result

A business application that treats security as a first-class requirement, with a written OWASP self-assessment, a UK GDPR data-protection analysis and a passing test suite: secure development end to end, not just a working feature set.

Limitations

There is no rate limiting or lockout on /login yet, so credential stuffing (A07) is only slowed by the password hash. The audit log is append-only by convention, not tamper-evident.