Map Threats in a Simple Login Service

CybersecurityBeginner
Practice Now

Introduction

Threat modeling is a practical way to think about what can go wrong before a system is attacked. Instead of starting with tools, you first identify what needs protection, where data crosses trust boundaries, how an attacker could move through the system, and which controls reduce the risk.

In this lab, you will inspect a small local login service and complete a structured threat model in security-model.json. The model is not a free-form report: each step adds specific JSON entries that can be checked by a validation script.

Inspect the Login Service

In this step, you will inspect the small service that you will model. A threat model starts from observable system behavior: what the application does, what files it uses, and what information could matter to an attacker.

First, move into the project directory:

cd /home/labex/project

List the prepared files:

ls -l

You should see files such as app.py, users.json, sessions.json, security-model.json, and the uploads directory.

Now ask the demo application to show its routes. A route is an application entry point that accepts some kind of request:

python3 app.py routes

The output should show four routes:

/login - accepts a username and password, then creates a session token
/files - lists files that belong to the logged-in user
/download - reads a requested filename from the uploads directory
/admin - shows account records to administrators only

Finally, run the current model checker. It will report missing sections because the model is intentionally empty:

python3 model-check.py all

You should see messages that each section is missing required entries. This is the starting point for the rest of the lab.

Identify the Assets

In this step, you will add assets to the model. In threat modeling, an asset is anything the system must protect. For this login service, the important assets are not only files; they also include authentication data and session state.

Use cat to inspect the user database:

cat users.json

The file contains usernames, passwords, and roles. That makes user_credentials and admin_records important assets.

Inspect the session file and the uploads directory:

cat sessions.json
ls -l uploads

The session file can contain login tokens, and the uploads directory contains private user content. Those map to session_tokens and private_uploads.

Now update the assets array in security-model.json. The command below uses a Python here-document. A here-document sends the lines between PY markers to python3 as a short script:

python3 - <<'PY'
import json
from pathlib import Path

path = Path("security-model.json")
model = json.loads(path.read_text())
model["assets"] = [
    "user_credentials",
    "session_tokens",
    "private_uploads",
    "admin_records",
]
path.write_text(json.dumps(model, indent=2) + "\n")
PY

Check only the assets section:

python3 model-check.py assets

The expected result is:

assets: complete

Map Trust Boundaries

In this step, you will add trust boundaries. A trust boundary is a place where data moves between areas with different levels of control or trust. Boundaries are useful because many attacks happen when untrusted input crosses into trusted code, files, or privileged functions.

Look again at the route list:

python3 app.py routes

The /login, /files, and /download routes receive input from a user-facing side and pass it into server logic. That is the browser_to_server boundary in this small model.

The /download route reads names from the request and then reaches into the uploads directory. That creates a server_to_filesystem boundary. The /admin route separates normal users from administrator-only data, so it creates a user_to_admin_area boundary.

Add these boundaries to the model:

python3 - <<'PY'
import json
from pathlib import Path

path = Path("security-model.json")
model = json.loads(path.read_text())
model["trust_boundaries"] = [
    "browser_to_server",
    "server_to_filesystem",
    "user_to_admin_area",
]
path.write_text(json.dumps(model, indent=2) + "\n")
PY

Check the trust boundary section:

python3 model-check.py trust_boundaries

The expected result is:

trust_boundaries: complete

Add Attack Paths

In this step, you will add attack paths. An attack path is a concrete way an attacker could move from an entry point toward an asset. This is the core of threat modeling: connecting assets and boundaries to realistic attacker behavior.

Try one login attempt with the demo credentials:

python3 app.py login alice wonderland

The output is a session token:

session-alice-demo

This behavior shows why passwords and session tokens matter. A weak password could enable credential_guessing, and a stolen token could enable session_token_theft.

The /download route accepts a requested filename, so a careless implementation could allow path_traversal. The /admin route protects administrator records, so missing role checks could allow admin_area_abuse.

Add these attack paths to the model:

python3 - <<'PY'
import json
from pathlib import Path

path = Path("security-model.json")
model = json.loads(path.read_text())
model["attack_paths"] = [
    "credential_guessing",
    "session_token_theft",
    "path_traversal",
    "admin_area_abuse",
]
path.write_text(json.dumps(model, indent=2) + "\n")
PY

Check the attack path section:

python3 model-check.py attack_paths

The expected result is:

attack_paths: complete

Choose Mitigations

In this step, you will finish the model by adding mitigations. A mitigation is a control that reduces the chance or impact of an attack path. Good threat models pair each important attack path with at least one practical control.

Use these pairings for the small login service:

  • credential_guessing can be reduced by rate_limit_login.
  • session_token_theft can be reduced by protect_session_tokens.
  • path_traversal can be reduced by validate_download_paths.
  • admin_area_abuse can be reduced by enforce_admin_authorization.

Add the mitigations to the model:

python3 - <<'PY'
import json
from pathlib import Path

path = Path("security-model.json")
model = json.loads(path.read_text())
model["mitigations"] = [
    "rate_limit_login",
    "protect_session_tokens",
    "validate_download_paths",
    "enforce_admin_authorization",
]
path.write_text(json.dumps(model, indent=2) + "\n")
PY

Now run the full model check:

python3 model-check.py all

Every section should be complete:

assets: complete
trust_boundaries: complete
attack_paths: complete
mitigations: complete

You can also view the final structured model:

cat security-model.json

The important result is that the model now connects protected assets, trust boundaries, attack paths, and mitigations for the same small service.

Summary

You practiced a practical threat modeling workflow on a small login service. You inspected the service, identified assets, mapped trust boundaries, added attack paths, and paired those paths with mitigations in a structured JSON model.

This workflow helps connect security thinking to observable system behavior instead of treating threat modeling as a separate written report.