IMPORTANT: This app contains intentional security vulnerabilities as part of a Capture The Flag (CTF) exercise focused on SQL injection and access control. Do not deploy as-is to production.
- Overview
- Quick Start
- Docker Usage
- Project Structure
- Features
- Tech Stack
- Architecture
- API Endpoints
- Database Schema
- Pages & Components
- CTF Challenges
- Development Workflow
- Security Policy
- Contributing
- License
SecureBank is a full‑stack banking simulation used for teaching web security. It models a realistic interface with login/registration, transactions, feedback, and an FAQ center — with deliberately vulnerable paths for SQLi and access control exercises.
- Prerequisites: Node.js 18+, npm 8+
Windows PowerShell:
cd frontend
npm ci
npm run dev
# Open http://localhost:3000macOS/Linux/WSL:
cd frontend
npm ci
npm run dev
# Open http://localhost:3000Default demo credentials (or register your own):
- Username:
admin| Password:admin123 - Username:
sunny.admin| Password:sunny.admin123
Environment:
- Create
frontend/.env.localwithCOOKIE_SECRET=...(a strong random string). Note: the repository includes a default secret for convenience; replace for demonstrations if desired.
From the repository root:
docker build -t securebank-ctf .Persist progress to a host directory or named volume:
Linux/macOS/WSL/Kali
docker run --rm -p 3000:3000 \
-v "$(pwd)/securebank-data:/app/data" \
--name securebank securebank-ctfWindows PowerShell
docker run --rm -p 3000:3000 `
-v ${PWD}/securebank-data:/app/data `
--name securebank securebank-ctfNamed Docker volume
docker volume create securebank-data
docker run --rm -p 3000:3000 \
-v securebank-data:/app/data \
--name securebank securebank-ctfNotes:
- First start seeds
/app/data/database.sqlitefrom a clean snapshot; subsequent restarts reuse the same DB. - Omit the volume for ephemeral demos (progress resets on container removal).
- Override cookie secret:
-e COOKIE_SECRET=your-secret. - Change port:
-e PORT=4000 -p 4000:4000.
securebank-ctf/
├─ challenges/
│ ├─ access_control_view_transactions/
│ ├─ admin_login_medium/
│ ├─ immediate_authority_medium/
│ ├─ privilege_escalation_medium/
│ └─ user_credentials_medium/
├─ frontend/
│ ├─ app/
│ │ ├─ api/{feedback,get-session,login,logout,register,transactions}
│ │ ├─ dashboard/{feedback,transactions,new}
│ │ ├─ help-faq/ • login/ • register/ • public/discussions/
│ │ └─ layout.tsx • page.tsx • globals.css
│ ├─ database/db.ts
│ ├─ lib/{utils.ts,next-connect.d.ts}
│ ├─ public/*
│ └─ package.json • tailwind.config.ts • tsconfig.json
├─ Dockerfile • start.sh
├─ README.md • CONTRIBUTING.md • CODE_OF_CONDUCT.md • SECURITY.md • Outline.md
└─ LICENSE
- Account management: login/logout, registration, session tracking
- Transactions: history, search/filter, add new entries
- Feedback: submit, list, and admin delete
- Help & FAQ: categorized content; intentionally vulnerable search
- Security model: signed session cookies, intentional SQLi/logic flaws for CTF
- Frontend: Next.js 15 (App Router), React 19, TypeScript, Tailwind CSS
- Backend: Next.js API routes,
better-sqlite3,cookie-signature - Data: SQLite (no ORM), in‑repo seeded DB for workshops
- Tooling: ESLint, Prettier, Docker
- App Router pages in
frontend/app/*with colocated route handlers inapp/api/* - SQLite via
better-sqlite3configured infrontend/database/db.ts - Auth via signed cookie containing
{ username, role }
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#3B82F6",
"primaryBorderColor": "#2563EB",
"primaryTextColor": "#ffffff",
"clusterBkg": "#F8FAFC",
"clusterBorder": "#94A3B8",
"lineColor": "#64748B"
}
}}%%
flowchart TB
Title["SecureBank CTF — Architecture<br/><span style='font-size:12px'>Diagram by Sunny Patel</span>"]
subgraph App["SecureBank CTF — Next.js 15 + SQLite"]
direction TB
Client["Client Pages<br/>/ • /login • /register<br/>/dashboard • /dashboard/transactions (+new)<br/>/dashboard/feedback • /help-faq"]
Server["App Router + UI<br/>Route Handlers (app/api/*)<br/>cookies/headers"]
API["API Routes<br/>POST login • POST logout • POST register<br/>GET get-session • GET/POST/DELETE feedback • GET/POST transactions"]
DBInit["database/db.ts<br/>schema init + admin seeding"]
SQLite[("SQLite<br/>database.sqlite")]
Client --> Server --> API --> SQLite
DBInit -. init/seed .- SQLite
subgraph Container["Containerization"]
Dockerfile["Dockerfile<br/>multi-stage build"]
Start["start.sh<br/>seed, symlink, run"]
Volume["Volume /app/data<br/>(persist)"]
end
Dockerfile --> Start --> SQLite
Volume --- SQLite
Start -. symlink/persist .- SQLite
end
classDef vuln fill:#fff3cd,stroke:#f0ad4e,color:#8a6d3b;
classDef title fill:#F8FAFC,stroke:#2563EB,color:#0F172A;
V1["Vulnerable surfaces<br/>feedback search • transactions search"]:::vuln
API --- V1
Title
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#3B82F6",
"primaryBorderColor": "#2563EB",
"primaryTextColor": "#ffffff",
"clusterBkg": "#F8FAFC",
"clusterBorder": "#94A3B8",
"lineColor": "#64748B"
}
}}%%
erDiagram
USERS {
int id PK
string username
string password
string role
}
TRANSACTIONS {
int id PK
int user_id FK
datetime date
string description
int amount
string type
}
FEEDBACK {
int id PK
string user
string message
datetime date
boolean read
}
USERS ||--o{ TRANSACTIONS : makes
USERS ||--o{ FEEDBACK : "submits (by username)"
Diagram by Sunny Patel
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#3B82F6",
"primaryBorderColor": "#2563EB",
"primaryTextColor": "#ffffff",
"clusterBkg": "#F8FAFC",
"clusterBorder": "#94A3B8",
"lineColor": "#64748B"
}
}}%%
sequenceDiagram
title SecureBank — Login Sequence
autonumber
participant U as User (Browser)
participant A as Next.js API (/api/login)
participant C as Cookie-Signature
participant DB as SQLite (database.sqlite)
U->>A: POST /api/login (username, password)
A->>A: Double-decode inputs
A->>DB: SELECT * FROM Users WHERE username AND password
alt Credentials match
A->>C: Sign session JSON with COOKIE_SECRET
C-->>A: Signed value
A-->>U: 200 OK + Set-Cookie session, userId
else Invalid
A-->>U: 401 Unauthorized
end
Note over U,A: Subsequent requests attach signed session cookie
U->>A: GET /api/get-session
A->>C: Unsign session with COOKIE_SECRET
C-->>A: Plain session JSON
A-->>U: 200 { username, role }
Note over U,DB: Diagram by Sunny Patel
POST /api/login•POST /api/logout•POST /api/registerGET /api/get-session•GET/POST /api/feedback•GET/POST /api/transactions
Example cookie creation (from login route):
const sessionData = JSON.stringify({ username: decodedUsername, role: user.role });
const signedSession = cookieSignature.sign(sessionData, process.env.COOKIE_SECRET!);
(await cookies()).set("session", signedSession, {
httpOnly: true, secure: process.env.NODE_ENV === "production",
sameSite: "strict", maxAge: 60 * 60 * 24, path: "/",
});Tables
CREATE TABLE IF NOT EXISTS Users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username VARCHAR(1000) UNIQUE,
password VARCHAR(1000),
role VARCHAR(1000)
);
CREATE TABLE IF NOT EXISTS Transactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sender VARCHAR(1000),
recipient VARCHAR(1000),
amount INTEGER
);
CREATE TABLE IF NOT EXISTS feedback (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user TEXT NOT NULL,
message TEXT NOT NULL,
date TEXT DEFAULT CURRENT_TIMESTAMP,
read BOOLEAN DEFAULT 0
);Seed admin if missing (excerpt):
const adminExists = db.prepare("SELECT * FROM Users WHERE username = 'admin'").get();
if (!adminExists) db.prepare("INSERT INTO Users (username, password, role) VALUES (?, ?, ?)").run("admin","admin123","admin");- Landing
/• Login/login• Register/register - Dashboard
/dashboardwith Transactions/dashboard/transactionsand Add New/dashboard/transactions/new - Feedback
/dashboard/feedback - Help & FAQ
/help-faq(intentionally vulnerable search)
Key components: navigation, transaction cards, FAQ accordion, search and form components.
- Access Control – View All Transactions (Medium): Trigger hidden dev logic and bypass per‑user filters to list all transactions. See
challenges/access_control_view_transactions. - Login as Admin via Double‑Encoded Injection (Medium): Bypass filters using URL‑double‑encoded payloads to log in as admin. See
challenges/admin_login_medium. - Immediate Authority (Medium): Inject via registration password to create an account with admin role. See
challenges/immediate_authority_medium. - Privilege Escalation via Feedback Injection (Medium): Stack queries via feedback to elevate your own role, then relogin. See
challenges/privilege_escalation_medium. - SQLi in Feedback Form – Dump Credentials (Medium): Exfiltrate
Userstable credentials by injecting into the feedback form. Seechallenges/user_credentials_medium.
- Code organization:
app(pages),app/api(endpoints),components,database,lib,globals.css - Adding features: create components/pages, add API handlers, update DB (if needed), test locally, commit with descriptive messages
- Practices: typed code, small focused components, responsive UI, semantic HTML, intentional unsanitized areas for CTF only
This repo intentionally includes insecure patterns for learning. For unintended issues, see SECURITY.md for responsible disclosure. Please do not open public exploit details unrelated to the intended challenges.
Contributions are welcome — new challenges, improvements, fixes. See CONTRIBUTING.md and adhere to the Code of Conduct (CODE_OF_CONDUCT.md).
MIT — see LICENSE.
Full Original README (Preserved) — click to expand
SecureBank is a comprehensive banking application simulation designed for educational purposes, particularly focusing on web security and SQL injection vulnerabilities. This application provides a realistic banking interface with features such as account management, transaction tracking, feedback system, and a detailed Help & FAQ section.
IMPORTANT NOTE: This application contains intentional security vulnerabilities as part of a Capture The Flag (CTF) exercise focused on SQL injection. These vulnerabilities are for educational purposes only and should not be implemented in production environments.
- Features
- Setup and Installation
- Running Locally
- Project Structure
- API and Backend
- Database Structure
- Security Vulnerabilities
- Pages and Components
- Technologies Used
- Development Workflow
- Deployment
- Contributing
- License
SecureBank offers a comprehensive set of features designed to simulate a real banking application:
- User authentication (login/logout)
- Account registration
- Profile management
- Session tracking (last login time)
- View transaction history
- Add new transactions (credit/debit)
- Search and filter transactions
- Transaction categorization
- Submit feedback on the application
- View feedback from other users
- Feedback history tracking
- Comprehensive FAQ section organized by categories
- Searchable FAQ database
- Quick tips and security recommendations
- Additional help resources
- Password protection
- Session management with signed cookies
- Secure routing
- Intentional SQL injection vulnerabilities for educational purposes
- Node.js (v18.0.0 or higher)
- npm (v8.0.0 or higher)
- Navigate to Project Directory (from Root):
cd frontend- Install dependencies:
npm ciNote: We use npm ci instead of npm install to ensure exact versions from package-lock.json are installed.
- Set up environment variables (Already comitted in Repo):
Create a
.env.localfile in the root directory with the following variables:COOKIE_SECRET=p9Y!2m@lK8z$1WqA7&dE4Xu0Cj
To run the application locally:
- Navigate to Project Directory (from Root):
cd frontend- Start the development server:
npm run dev-
Access the application: Open your browser and navigate to
http://localhost:3000 -
Login credentials: For testing purposes, you can use the following credentials:
- Username:
admin - Password:
admin123
or
- Username:
sunny.admin - Password:
sunny.admin123
Or register a new account through the registration page.
- Username:
To provide a consistent target for workshops or CTF events, you can run SecureBank in a container. The image bakes in a pristine copy of the SQLite database while still allowing each container instance to persist its own progress across restarts.
-
Build the image (from the repository root):
docker build -t securebank-ctf . -
Start the container using the volume syntax that matches your platform. All examples below persist progress in a folder or volume named
securebank-data:-
Linux / macOS / WSL / Kali
docker run --rm -p 3000:3000 \ -v "$(pwd)/securebank-data:/app/data" \ --name securebank securebank-ctf -
Windows PowerShell
docker run --rm -p 3000:3000 ` -v ${PWD}/securebank-data:/app/data ` --name securebank securebank-ctf
-
Named Docker volume (works everywhere)
docker volume create securebank-data docker run --rm -p 3000:3000 \ -v securebank-data:/app/data \ --name securebank securebank-ctf
The first time the container starts it seeds
/app/data/database.sqlitefrom a clean snapshot. Subsequent restarts reuse the same database file, so solved challenges and created users remain intact until you delete the mounted directory or volume. -
-
Access the application at
http://localhost:3000using the credentials above or create your own accounts.
Tips:
- You can omit the volume flag for stateless demos—the app will still boot with the seeded database but progress will reset whenever the container is removed.
- Override the intentionally insecure cookie secret by passing
-e COOKIE_SECRET=your-secret-heretodocker run.- Change the published port by supplying
-e PORT=4000 -p 4000:4000.
frontend/
├── app/ # Next.js App Router directory
│ ├── api/ # API routes for backend functionality
│ │ ├── feedback/ # Feedback API endpoints
│ │ │ └── route.ts
│ │ ├── get-session/ # Session management endpoints
│ │ │ └── route.ts
│ │ ├── login/ # Authentication endpoints
│ │ │ └── route.ts
│ │ ├── logout/ # Logout functionality
│ │ │ └── route.ts
│ │ ├── register/ # User registration
│ │ │ └── route.ts
│ │ └── transactions/ # Transaction management
│ │ └── route.ts
│ ├── dashboard/ # Dashboard pages
│ │ ├── feedback/ # Feedback system
│ │ │ └── page.tsx
│ │ ├── transactions/ # Transaction management
│ │ │ ├── add/
│ │ │ │ └── page.tsx # Add transaction form
│ │ │ └── page.tsx # Transaction list
│ │ └── page.tsx # Main dashboard
│ ├── help-faq/ # Help & FAQ section
│ │ └── page.tsx
│ ├── login/ # Login page
│ │ └── page.tsx
│ ├── public/ # Public discussions page
│ │ └── discussions/
│ │ └── page.tsx
│ ├── register/ # Registration page
│ │ └── page.tsx
│ ├── globals.css # Global CSS styles
│ ├── layout.tsx # Root layout
│ └── page.tsx # Landing page
├── components/ # Shared components (not shown in file structure)
│ ├── ui/ # UI components
│ └── ...
├── database/ # Database configuration and setup
│ └── db.ts # SQLite database initialization
├── lib/ # Utility functions and libraries
│ └── utils.ts
├── public/ # Static assets
│ ├── digital-flow.png
│ ├── interconnected-banking.png
│ └── secure-future-hands.png
├── .env.local # Environment variables
├── .eslintrc.json # ESLint configuration
├── .gitignore # Git ignore file
├── database.sqlite # SQLite database file
├── next.config.mjs # Next.js configuration
├── package.json # Project dependencies
├── README.md # Project documentation
├── tailwind.config.ts # Tailwind CSS configuration
└── tsconfig.json # TypeScript configuration
└── Dockerfile # Build structset
└── .dockerignore # Docker ignore file (node_modules, etc.)
└── start.sh # Start script for Docker
The application uses Next.js App Router API routes to handle backend functionality. Each API endpoint is implemented as a route handler in the app/api directory.
/api/feedback: Manages user feedback submissions and retrieval/api/get-session: Handles session management and user information retrieval/api/login: Authenticates users and creates secure sessions/api/logout: Terminates user sessions/api/register: Handles new user registration/api/transactions: Manages transaction creation and retrieval
The application uses signed cookies for secure authentication:
- User submits login credentials
- Server validates credentials against the database
- On successful authentication, a signed session cookie is created using cookie-signature
- Session information includes username and role
- Protected routes verify the session cookie before granting access
Example from the login route handler:
// Create signed session cookie
const sessionData = JSON.stringify({ username: decodedUsername, role: user.role });
const secret = process.env.COOKIE_SECRET!;
const signedSession = cookieSignature.sign(sessionData, secret);
// Set the cookie
(await cookies()).set("session", signedSession, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "strict",
maxAge: 60 * 60 * 24, // 1-day expiration
path: "/",
});The application uses SQLite with better-sqlite3 for data storage. The database schema is defined in database/db.ts:
- Users
CREATE TABLE IF NOT EXISTS Users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username VARCHAR(1000) UNIQUE,
password VARCHAR(1000),
role VARCHAR(1000)
);- Transactions
CREATE TABLE IF NOT EXISTS Transactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sender VARCHAR(1000),
recipient VARCHAR(1000),
amount INTEGER
);- Feedback
CREATE TABLE IF NOT EXISTS feedback (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user TEXT NOT NULL,
message TEXT NOT NULL,
date TEXT DEFAULT CURRENT_TIMESTAMP,
read BOOLEAN DEFAULT 0
);The database is initialized with a default admin user if one doesn't exist:
// Inserts a hardcoded admin user if none exists
const adminExists = db.prepare("SELECT * FROM Users WHERE username = 'admin'").get();
if (!adminExists) {
db.prepare("INSERT INTO Users (username, password, role) VALUES (?, ?, ?)").run(
"admin",
"admin123",
"admin"
);
console.log("Admin user created: admin / admin123");
} else {
console.log("Admin user already exists.");
}-
Landing Page (
/):- Introduction to SecureBank
- Login and registration links
- Animated background using Vanta.js
-
Login Page (
/login):- User authentication form
- Error handling for invalid credentials
- Link to registration page
-
Registration Page (
/register):- New user registration form
- Password confirmation
- Form validation
-
Dashboard (
/dashboard):- Account overview
- Recent transactions
- Financial statistics
- Quick links to other sections
-
Transactions (
/dashboard/transactions):- Transaction history table
- Search and filter functionality
- Transaction summary cards
-
Add Transaction (
/dashboard/transactions/new):- Form to add new transactions
- Date picker
- Transaction type selection
-
Feedback (
/dashboard/feedback):- Feedback submission form
- Feedback history
- Statistics on feedback
-
Help & FAQ (
/help-faq):- Categorized FAQ sections
- Search functionality (vulnerable to SQL injection)
- Contact information
-
Navigation Bar:
- Present on all authenticated pages
- Links to main sections
- Logout button
-
Transaction Cards:
- Display transaction information
- Color-coded by transaction type
-
FAQ Accordion:
- Expandable FAQ items
- Category filtering
-
Search Components:
- Present in transactions and FAQ pages
- Vulnerable to SQL injection (intentionally)
-
Form Components:
- Login, registration, transaction, and feedback forms
- Input validation
- Next.js: React framework for server-rendered applications
- React: JavaScript library for building user interfaces
- TypeScript: Typed superset of JavaScript
- Tailwind CSS: Utility-first CSS framework
- Vanta.js: 3D animated backgrounds on login screen.
- Next.js API Routes: Server-side API endpoints
- better-sqlite3: SQLite database driver for Node.js
- cookie-signature: For signing and verifying cookies
- next/headers: For cookie management in route handlers
- ESLint: JavaScript linting utility
- Prettier: Code formatter
- Git: Version control system
- Pages: Located in the
appdirectory following Next.js App Router conventions - API Routes: Server-side endpoints in the
app/apidirectory - Components: Reusable UI elements in the
componentsdirectory - Database: SQLite setup and schema in the
databasedirectory - Styles: Tailwind CSS utility classes with global styles in
globals.css - Utils: Helper functions in the
libdirectory
- Create new components in the
componentsdirectory - Add new pages in the appropriate directories under
app - Implement API endpoints in the
app/apidirectory - Update database schema if necessary at
database/db.ts - Test the feature locally
- Commit changes with descriptive commit messages
- Follow TypeScript type definitions
- Use React hooks for state management
- Implement responsive design using Tailwind's responsive utilities
- Keep components small and focused on a single responsibility
- Use semantic HTML elements
- Properly sanitize user inputs in production code (intentionally not done in some places for CTF)
This project is licensed under the MIT License - see the LICENSE file for details.
This application contains intentional security vulnerabilities for educational purposes. Do not use this code in production environments without addressing these vulnerabilities. The creators of this application are not responsible for any misuse or damage caused by the code.
Last updated: April 15, 2025
Created for educational purposes only for CSCI3540U - Ontario Tech - CTF Final Major Project.