Prestige Rentals: A Secure Full-Stack Application

Project Overview

Prestige Rentals is a full-stack car rental web application that I designed and developed from the ground up. My primary objective was not only to create a functional platform but to architect it with security and resilience as core design principles. As a cybersecurity student, I approached this project as both a software engineer and a security analyst—prioritizing safe coding practices across every layer, from the database schema to the client-side logic.

The platform serves two distinct roles: Customers, who can browse, rent, and review vehicles, and Administrators, who oversee fleet management, user accounts, and rental activity. Supporting these roles required a well-defined system for authentication, authorization, and data protection.

How It Works: Core Functionality

The application is powered by a modular React (JSX) front end that communicates with a custom Node.js (Express) backend API and a MySQL database as the persistent data store.

The Customer Experience

Guests can explore the car catalog and apply filters to refine their search. To rent or interact with listings, users must first create an account. Once authenticated, they gain access to several key features:

  • Dashboard: A personalized dashboard for managing user profiles, saved vehicles, rental history, and payment options.
  • Rental Flow: A guided multi-step rental process that includes date selection, terms of service acknowledgment, optional add-ons, and payment confirmation.
  • Reviews: Verified renters can submit one review per vehicle and have full control to edit or delete their own feedback.

The Admin Panel

Administrators access a dedicated portal that grants elevated privileges through role-based API tokens. From here, staff can:

  • Perform full CRUD (Create, Read, Update, Delete) operations on vehicle listings, customer records, and employee accounts.
  • Manage maintenance schedules and monitor system-wide rental metrics.

Database Features

The Prestige Rentals backend is designed with robust, secure, and highly functional features that support both business logic and cybersecurity best practices. Key capabilities of the system include:

Security & Server Setup

  • Secure HTTP Headers using helmet.
  • CORS restricted to specific origins.
  • Environment variables for sensitive data management.
  • Rate limiting for login, registration, and password reset endpoints.
  • Connection pooling to MySQL via mysql2/promise.

User & Employee Authentication

  • Employee and customer login endpoints.
  • Customer registration with duplicate email checks.
  • Password hashing using bcrypt.
  • Password reset system using secure, expiring tokens.
  • JWT-based authentication for admin and customer roles.
  • Optional authentication for customizing public data access.

Public Data & Car Browsing

  • Endpoints for featured cars and testimonials.
  • Listing of car types, features, and rental add-ons.
  • Filter and search cars with type, make/model, and average rating.
  • Fetch complete car details and reviews, including user-specific flags.

Customer Profile & Management (Authenticated)

  • View and update customer profile and password.
  • Manage payment methods (add, list, delete) with ownership checks.
  • Manage favorites (add, list, remove).
  • Review system with eligibility checks and CRUD operations.
  • Access to active and past rental history.

Rental Workflow (Authenticated)

  • Create rentals using database transactions for atomic operations.
  • Calculate total cost including add-ons and duration.
  • Return rentals safely with status updates and optional fees.
  • Delete completed rentals along with associated payments and add-ons.

Admin Panel (Authenticated)

  • Dashboard metrics: car availability, revenue, and popular car types.
  • CRUD operations for cars, employees, and customers.
  • Maintenance management: schedule, complete, update costs, and history tracking.
  • Force-return rentals and manage active maintenance records.

Database & Backend Deep Dive

The backend of Prestige Rentals is designed around a relational MySQL database, which serves as the backbone for all persistent data. Every piece of data—from vehicle information to customer profiles—is stored in structured tables with clear relationships, allowing for consistent, safe, and efficient queries. Let’s break down the architecture and implementation.

1. Database Schema & Table Design

The schema models real-world entities, ensuring clarity and maintainability:

  • Customers: Stores essential user information including customer_id, name, email, hashed_password, role, and account status. Passwords are hashed using bcrypt before being stored to prevent compromise.
  • Cars: Maintains details about each vehicle, including car_id, make, model, year, license_plate, availability_status, and optional maintenance schedules.
  • Rentals: Maps customers to vehicles with rental_id, customer_id, car_id, start_date, end_date, status, and total_price.
  • Payments: Tracks payment transactions per rental, storing payment_id, rental_id, amount, payment_method, timestamp, and status.
  • Reviews: Connects customers to vehicles they’ve rented, with review_id, customer_id, car_id, rating, comment, and created_at. Each customer may submit only one review per vehicle.

2. Relationships & Referential Integrity

Proper relational mapping ensures data consistency:

  • Rentals.customer_id → Customers.customer_id (foreign key constraint enforces that every rental belongs to a valid customer).
  • Rentals.car_id → Cars.car_id (a rental must reference an existing car).
  • Payments.rental_id → Rentals.rental_id (payments are always linked to rentals).
  • Reviews.customer_id → Customers.customer_id and Reviews.car_id → Cars.car_id (reviews must belong to valid customers and cars).

Using foreign keys enforces referential integrity, preventing orphaned records. Cascading rules, such as ON DELETE CASCADE for rentals and reviews, ensure that when a customer or vehicle is removed, related records are automatically cleaned up.

3. Indexing & Query Optimization

To support fast lookups and complex joins, I implemented indexing strategies:

  • Primary keys automatically create unique indexes on customer_id, car_id, rental_id, etc.
  • Secondary indexes on email, license_plate, and start_date/end_date optimize search queries and filter operations.
  • Composite indexes on customer_id, car_id in Rentals improve performance for queries like "all rentals for a given customer" or "all rentals of a specific car."

4. SQL Queries & Parameterization

All SQL queries are implemented using mysql2/promise with parameterized statements to prevent SQL injection. Examples:

  • Retrieve active rentals for a customer:
    SELECT r.rental_id, c.make, c.model, r.start_date, r.end_date
    FROM Rentals r
    JOIN Cars c ON r.car_id = c.car_id
    WHERE r.customer_id = ? AND r.status = 'active';
  • Create a new rental:
    INSERT INTO Rentals (customer_id, car_id, start_date, end_date, total_price)
    VALUES (?, ?, ?, ?, ?);
  • Update vehicle availability after rental:
    UPDATE Cars SET availability_status = 'unavailable' WHERE car_id = ?;

5. Transactions & Data Consistency

For multi-step operations like rental creation and payment processing, I use database transactions to ensure atomicity. This guarantees that either all steps complete successfully or none are applied, preventing inconsistent states. Example flow:

  1. Start transaction.
  2. Insert rental record.
  3. Insert payment record.
  4. Update car availability.
  5. Commit transaction if all succeed; otherwise, rollback.

6. Backend API & Integration

The backend exposes RESTful endpoints that abstract direct SQL operations, enforce validation, and provide secure data access:

  • POST /api/rentals – Creates a new rental, validates car availability, and initiates a transaction for payment and availability updates.
  • GET /api/customers/:id/rentals – Returns a customer’s rental history, joining Rentals with Cars for readable output.
  • GET /api/admin/cars – Provides admin-only vehicle management data, enforcing RBAC via middleware.
  • POST /api/reviews – Adds a review after verifying rental ownership, ensuring only verified customers can submit feedback.

7. Data Security & Privacy

Beyond SQL injection prevention, sensitive data such as passwords and payment information are never returned to the client. Query results are sanitized, and all data mutations are logged for auditing. User access is strictly controlled using JWTs and role-based middleware.

This backend architecture provides a robust, secure, and scalable foundation for the rental website, supporting both business logic and cybersecurity best practices.

Entity-Relationship (ER) Diagram

Below is a textual representation of the database schema in ER diagram format, detailing entities, weak entities, and relationships used in Prestige Rentals.

Entities (Strong Entities)

  • Customers
    • Attributes: customer_id (PK), first_name, last_name, email, phone_number, address, city, state, zip_code, date_of_birth, password, reset_token, reset_token_expires
  • Cars
    • Attributes: car_id (PK), make, model, year, license_plate, daily_rate, status, image_url, mileage, next_service_due_date, next_service_details, purchase_date, previous_owners, car_type_id (FK), location_id (FK)
  • Employees
    • Attributes: employee_id (PK), first_name, last_name, job_title, hire_date, email, username, password
  • Rentals
    • Attributes: rental_id (PK), customer_id (FK), car_id (FK), pickup_date, due_date, return_date, total_cost, late_fee
  • Car_Types
    • Attributes: car_type_id (PK), type_name
  • Locations
    • Attributes: location_id (PK), name, address
  • Addons
    • Attributes: addon_id (PK), name, description, price
  • Features
    • Attributes: feature_id (PK), name

Weak Entities

  • Payments – existence dependent on Rentals
    • Attributes: payment_id (PK), rental_id (FK), amount, payment_date, payment_method
  • Maintenance – existence dependent on Cars
    • Attributes: maintenance_id (PK), car_id (FK), service_date, service_type, cost, notes, completion_date
  • CustomerPaymentMethods – existence dependent on Customers
    • Attributes: payment_method_id (PK), customer_id (FK), card_holder_name, card_type, masked_number, expiry_date, is_primary
  • Reviews – existence dependent on Customers and Cars
    • Attributes: review_id (PK), car_id (FK), customer_id (FK), rating, review_text, review_date

Relationships

  • books – connects Customers to Rentals (1:N)
  • is_for – connects Rentals to Cars (N:1)
  • is_type_of – connects Cars to Car_Types (N:1)
  • is_parked_at – connects Cars to Locations (N:1)
  • favorites – connects Customers to Cars (N:M)
  • has_features – connects Cars to Features (N:M)
  • includes_addons – connects Rentals to Addons (N:M)

Identifying Relationships (for Weak Entities)

  • generates – Rentals to Payments (1:N)
  • undergoes – Cars to Maintenance (1:N)
  • has_method – Customers to CustomerPaymentMethods (1:N)
  • writes_review – Customers to Reviews (1:N)
  • receives_review – Cars to Reviews (1:N)

Cybersecurity Implementation: A Defensive Deep Dive

Security was the guiding principle behind every architectural decision. Drawing from my coursework and practical labs, I implemented multiple layers of defense to mitigate common web application vulnerabilities.

1. Authentication & Session Management

  • Password Hashing: User passwords are never stored in plaintext. Using bcrypt, each password is hashed with a unique salt to defend against rainbow table and dictionary attacks.
  • JSON Web Tokens (JWT): Upon successful login, the server issues a signed, stateless JWT containing the user’s ID and role. This token is stored in sessionStorage and attached to all subsequent requests via the Authorization header.

2. Authorization & Access Control

Authentication verifies identity, but authorization enforces boundaries. I implemented Role-Based Access Control (RBAC) using Express middleware.

  • Middleware Validation: Two custom middleware functions—authenticateCustomer and authenticateAdmin—validate JWTs and confirm user roles before granting access.
  • Route Protection: Administrative routes (e.g., /api/admin/cars) are restricted to valid admin tokens. Any unauthorized request receives an immediate 403 Forbidden response.

3. Mitigating Common Vulnerabilities

  • SQL Injection: All database queries use mysql2/promise with parameterized queries, ensuring user input is treated strictly as data and not executable SQL code.
  • Brute-Force Protection: The authentication endpoints employ express-rate-limit to throttle repeated failed login attempts, mitigating automated password guessing attacks.
  • HTTP Header Hardening: helmet.js is configured to enforce headers like Strict-Transport-Security, X-Frame-Options, and X-Content-Type-Options, reducing exposure to clickjacking and MIME-sniffing exploits.
  • Cross-Origin Resource Sharing (CORS): The backend only accepts requests from whitelisted origins, preventing malicious cross-domain API calls.

4. Secure API & Data Handling

  • Data Ownership Verification: Before performing destructive actions (e.g., deleting reviews or payment methods), the backend validates ownership by matching the resource’s user ID against the ID from the verified JWT.
  • Response Sanitization: All user-facing responses are scrubbed of sensitive data such as password hashes and tokens before being returned to the client.

Technology Stack

  • Frontend: React (JSX), Astro, Tailwind CSS, Framer Motion
  • Backend: Node.js, Express.js
  • Database: MySQL with mysql2/promise
  • Security & Auth: JSON Web Tokens (JWT), bcrypt, helmet.js, express-rate-limit, cors

What I Learned

Developing Prestige Rentals was an opportunity to bridge theory and practice in secure software design. Concepts like SQL injection, rate limiting, and RBAC moved from abstract lessons to real-world implementations that I could test and validate.

This project reinforced the mindset of building defensively—never trusting client input, always validating on the server, and designing APIs to minimize exposure. It deepened my understanding of how security is not a single feature, but a process integrated throughout the software lifecycle.