From a8a42b752e6384ee5ed8fc37e32b0954de960b0a Mon Sep 17 00:00:00 2001 From: ashuno Date: Fri, 12 Jun 2026 23:25:28 +0300 Subject: [PATCH] feat(lab2): add threat model baseline and secure variant --- labs/lab2/threagile-model-secure.yaml | 429 ++++++++++++++++++++++++++ submissions/lab2.md | 101 ++++++ 2 files changed, 530 insertions(+) create mode 100644 labs/lab2/threagile-model-secure.yaml create mode 100644 submissions/lab2.md diff --git a/labs/lab2/threagile-model-secure.yaml b/labs/lab2/threagile-model-secure.yaml new file mode 100644 index 000000000..7f8b311e4 --- /dev/null +++ b/labs/lab2/threagile-model-secure.yaml @@ -0,0 +1,429 @@ +threagile_version: 1.0.0 + +title: OWASP Juice Shop Threat Model +date: 2025-09-18 + +author: + name: Student Name + homepage: https://example.edu + +management_summary_comment: > + Threat model for a local OWASP Juice Shop setup. Users access the app + either directly via HTTP on port 3000 or through an optional reverse proxy that + terminates TLS and adds security headers. The app runs in a container + and writes data to a host-mounted volume (for database, uploads, logs). + Optional outbound notifications (e.g., a challenge-solution WebHook) can be configured for integrations. + +business_criticality: important # archive, operational, important, critical, mission-critical + +business_overview: + description: > + Training environment for DevSecOps. This model covers a deliberately vulnerable + web application (OWASP Juice Shop) running locally in a Docker container. The focus is on a minimal architecture, STRIDE threat analysis, and actionable mitigations for the identified risks. + + images: + # - dfd.png: Data Flow Diagram (if exported from the tool) + +technical_overview: + description: > + A user’s web browser connects to the Juice Shop application (Node.js/Express server) either directly on **localhost:3000** (HTTP) or via a **reverse proxy** on ports 80/443 (with HTTPS). The Juice Shop server may issue outbound requests to external services (e.g., a configured **WebHook** for solved challenge notifications). All application data (the SQLite database, file uploads, logs) is stored on the host’s filesystem via a mounted volume. Key trust boundaries include the **Internet** (user & external services) → **Host** (local machine/VM) → **Container Network** (isolated app container). + images: [] + +questions: + Do you expose port 3000 beyond localhost?: "" + Do you use a reverse proxy with TLS and security headers?: "" + Are any outbound integrations (webhooks) configured?: "" + Is any sensitive data stored in logs or files?: "" + +abuse_cases: + Credential Stuffing / Brute Force: > + Attackers attempt repeated login attempts to guess credentials or exhaust system resources. + Stored XSS via Product Reviews: > + Malicious scripts are inserted into product reviews, getting stored and executed in other users’ browsers. + SSRF via Outbound Requests: > + Server-side requests (e.g. profile image URL fetch or WebHook callback) are abused to access internal network resources. + +security_requirements: + TLS in transit: Enforce HTTPS for user traffic via a TLS-terminating reverse proxy with strong ciphers and certificate management. + AuthZ on sensitive routes: Implement strict server-side authorization checks (role/permission) on admin or sensitive functionalities. + Rate limiting & lockouts: Apply rate limiting and account lockout policies to mitigate brute-force and automated attacks on authentication and expensive operations. + Secure headers: Add security headers (HSTS, CSP, X-Frame-Options, X-Content-Type-Options, etc.) at the proxy or app to mitigate client-side attacks. + Secrets management: Protect secret keys and credentials (JWT signing keys, OAuth client secrets) – keep them out of code repos and avoid logging them. + +tags_available: + # Relevant technologies and environment tags + - docker + - nodejs + # Data and asset tags + - pii + - auth + - tokens + - logs + - public + - actor + - user + - optional + - proxy + - app + - storage + - volume + - saas + - webhook + # Communication tags + - primary + - direct + - egress + +# ========================= +# DATA ASSETS +# ========================= +data_assets: + + User Accounts: + id: user-accounts + description: "User profile data, credential hashes, emails." + usage: business + tags: ["pii", "auth"] + origin: user-supplied + owner: Lab Owner + quantity: many + confidentiality: confidential + integrity: critical + availability: important + justification_cia_rating: > + Contains personal identifiers and authentication data. High confidentiality is required to protect user privacy, and integrity is critical to prevent account takeovers. + + Orders: + id: orders + description: "Order history, addresses, and payment metadata (no raw card numbers)." + usage: business + tags: ["pii"] + origin: application + owner: Lab Owner + quantity: many + confidentiality: confidential + integrity: important + availability: important + justification_cia_rating: > + Contains users’ personal data and business transaction records. Integrity and confidentiality are important to prevent fraud or privacy breaches. + + Product Catalog: + id: product-catalog + description: "Product information (names, descriptions, prices) available to all users." + usage: business + tags: ["public"] + origin: application + owner: Lab Owner + quantity: many + confidentiality: public + integrity: important + availability: important + justification_cia_rating: > + Product data is intended to be public, but its integrity is important (to avoid defacement or price manipulation that could mislead users). + + Tokens & Sessions: + id: tokens-sessions + description: "Session identifiers, JWTs for authenticated sessions, CSRF tokens." + usage: business + tags: ["auth", "tokens"] + origin: application + owner: Lab Owner + quantity: many + confidentiality: confidential + integrity: important + availability: important + justification_cia_rating: > + If session tokens are compromised, attackers can hijack user sessions. They must be kept confidential and intact; availability is less critical (tokens can be reissued). + + Logs: + id: logs + description: "Application and access logs (may inadvertently contain PII or secrets)." + usage: devops + tags: ["logs"] + origin: application + owner: Lab Owner + quantity: many + confidentiality: internal + integrity: important + availability: important + justification_cia_rating: > + Logs are for internal use (troubleshooting, monitoring). They should not be exposed publicly, and sensitive data should be sanitized to protect confidentiality. + +# ========================= +# TECHNICAL ASSETS +# ========================= +technical_assets: + + User Browser: + id: user-browser + description: "End-user web browser (client)." + type: external-entity + usage: business + used_as_client_by_human: true + out_of_scope: false + justification_out_of_scope: + size: system + technology: browser + tags: ["actor", "user"] + internet: true + machine: virtual + encryption: none + owner: External User + confidentiality: public + integrity: operational + availability: operational + justification_cia_rating: "Client controlled by end user (potentially an attacker)." + multi_tenant: false + redundant: false + custom_developed_parts: false + data_assets_processed: [] + data_assets_stored: [] + data_formats_accepted: + - json + communication_links: + To Reverse Proxy (preferred): + target: reverse-proxy + description: "User browser to reverse proxy (HTTPS on 443)." + protocol: https + authentication: session-id + authorization: enduser-identity-propagation + tags: ["primary"] + vpn: false + ip_filtered: false + readonly: false + usage: business + data_assets_sent: + - tokens-sessions + data_assets_received: + - product-catalog + Direct to App (no proxy): + target: juice-shop + description: "Direct browser access to app (HTTP on 3000)." + protocol: https + authentication: session-id + authorization: enduser-identity-propagation + tags: ["direct"] + vpn: false + ip_filtered: false + readonly: false + usage: business + data_assets_sent: + - tokens-sessions + data_assets_received: + - product-catalog + + Reverse Proxy: + id: reverse-proxy + description: "Optional reverse proxy (e.g., Nginx) for TLS termination and adding security headers." + type: process + usage: business + used_as_client_by_human: false + out_of_scope: false + justification_out_of_scope: + size: application + technology: reverse-proxy + tags: ["optional", "proxy"] + internet: false + machine: virtual + encryption: transparent + owner: Lab Owner + confidentiality: internal + integrity: important + availability: important + justification_cia_rating: "Not exposed to internet directly; improves security of inbound traffic." + multi_tenant: false + redundant: false + custom_developed_parts: false + data_assets_processed: + - product-catalog + - tokens-sessions + data_assets_stored: [] + data_formats_accepted: + - json + communication_links: + To App: + target: juice-shop + description: "Proxy forwarding to app (HTTP on 3000 internally)." + protocol: https + authentication: none + authorization: none + tags: [] + vpn: false + ip_filtered: false + readonly: false + usage: business + data_assets_sent: + - tokens-sessions + data_assets_received: + - product-catalog + + Juice Shop Application: + id: juice-shop + description: "OWASP Juice Shop server (Node.js/Express, v19.0.0). All database queries use parameterized statements (prepared statements) to prevent SQL injection." + type: process + usage: business + used_as_client_by_human: false + out_of_scope: false + justification_out_of_scope: + size: application + technology: web-server + tags: ["app", "nodejs"] + internet: false + machine: container + encryption: none + owner: Lab Owner + confidentiality: internal + integrity: important + availability: important + justification_cia_rating: "In-scope web application (contains all business logic and vulnerabilities by design)." + multi_tenant: false + redundant: false + custom_developed_parts: true + data_assets_processed: + - user-accounts + - orders + - product-catalog + - tokens-sessions + data_assets_stored: + - logs + data_formats_accepted: + - json + communication_links: + To Challenge WebHook: + target: webhook-endpoint + description: "Optional outbound callback (HTTP POST) to external WebHook when a challenge is solved." + protocol: https + authentication: none + authorization: none + tags: ["egress"] + vpn: false + ip_filtered: false + readonly: false + usage: business + data_assets_sent: + - orders + + Persistent Storage: + id: persistent-storage + description: "Host-mounted volume for database, file uploads, and logs." + type: datastore + usage: devops + used_as_client_by_human: false + out_of_scope: false + justification_out_of_scope: + size: component + technology: file-server + tags: ["storage", "volume"] + internet: false + machine: virtual + encryption: data-with-symmetric-shared-key + owner: Lab Owner + confidentiality: internal + integrity: important + availability: important + justification_cia_rating: "Local disk storage for the container – not directly exposed, but if compromised it contains sensitive data (database and logs)." + multi_tenant: false + redundant: false + custom_developed_parts: false + data_assets_processed: [] + data_assets_stored: + - logs + - user-accounts + - orders + - product-catalog + data_formats_accepted: + - file + communication_links: {} + + Webhook Endpoint: + id: webhook-endpoint + description: "External WebHook service (3rd-party, if configured for integrations)." + type: external-entity + usage: business + used_as_client_by_human: false + out_of_scope: true + justification_out_of_scope: "Third-party service to receive notifications (not under our control)." + size: system + technology: web-service-rest + tags: ["saas", "webhook"] + internet: true + machine: virtual + encryption: none + owner: Third-Party + confidentiality: internal + integrity: operational + availability: operational + justification_cia_rating: "External service that receives data (like order or challenge info). Treated as a trusted integration point but could be abused if misconfigured." + multi_tenant: true + redundant: true + custom_developed_parts: false + data_assets_processed: + - orders + data_assets_stored: [] + data_formats_accepted: + - json + communication_links: {} + +# ========================= +# TRUST BOUNDARIES +# ========================= +trust_boundaries: + + Internet: + id: internet + description: "Untrusted public network (Internet)." + type: network-dedicated-hoster + tags: [] + technical_assets_inside: + - user-browser + - webhook-endpoint + trust_boundaries_nested: + - host + + Host: + id: host + description: "Local host machine / VM running the Docker environment." + type: network-dedicated-hoster + tags: [] + technical_assets_inside: + - reverse-proxy + - persistent-storage + trust_boundaries_nested: + - container-network + + Container Network: + id: container-network + description: "Docker container network (isolated internal network for containers)." + type: network-dedicated-hoster + tags: [] + technical_assets_inside: + - juice-shop + trust_boundaries_nested: [] + +# ========================= +# SHARED RUNTIMES +# ========================= +shared_runtimes: + + Docker Host: + id: docker-host + description: "Docker Engine and default bridge network on the host." + tags: ["docker"] + technical_assets_running: + - juice-shop + # If the reverse proxy is containerized, include it: + # - reverse-proxy + +# ========================= +# INDIVIDUAL RISK CATEGORIES (optional) +# ========================= +individual_risk_categories: {} + +# ========================= +# RISK TRACKING (optional) +# ========================= +risk_tracking: {} + +# (Optional diagram layout tweaks can be added here) +#diagram_tweak_edge_layout: spline +#diagram_tweak_layout_left_to_right: true diff --git a/submissions/lab2.md b/submissions/lab2.md new file mode 100644 index 000000000..77f9a2b2f --- /dev/null +++ b/submissions/lab2.md @@ -0,0 +1,101 @@ +# Lab 2 — Threat Modeling: STRIDE on Juice Shop with Threagile + +## Task 1: Baseline Threat Model + +### Risk count by severity + +| Severity | Count | +|----------|-------| +| elevated | 4 | +| low | 5 | +| medium | 14 | +| Total | 23 | + +### Top 5 risks + +1. **missing-authentication** — no authentication between Reverse Proxy and Juice Shop; severity elevated; affects juice-shop + +2. **unencrypted-communication** — browser connects directly to Juice Shop over HTTP, sending credentials in plain text; severity elevated; affects user-browser + +3. **unencrypted-communication** — Reverse Proxy connects to Juice Shop over HTTP; severity elevated; affects reverse-proxy + +4. **cross-site-scripting** — Juice Shop has XSS vulnerabilities; severity elevated; affects juice-shop + +5. **missing-waf** — no web application firewall; severity low; affects juice-shop + +### STRIDE mapping + +1. **missing-authentication** — STRIDE: S (Spoofing) + E (Elevation of Privilege). Without authentication between the proxy and the app, an attacker on the same network could pretend to be the proxy and send any requests to Juice Shop, or bypass the proxy entirely. + +2. **unencrypted-communication (Direct to App)** — STRIDE: I (Information Disclosure) + T (Tampering). HTTP sends credentials like JWTs and session IDs in plain text. Someone on the same network can capture them or modify requests while they are in transit. + +3. **unencrypted-communication (To App via Proxy)** — STRIDE: I (Information Disclosure). Even with a reverse proxy, the internal connection from proxy to app uses HTTP. If an attacker compromises the host machine, they can read this internal traffic. + +4. **cross-site-scripting** — STRIDE: T (Tampering) + I (Information Disclosure). XSS lets an attacker inject malicious scripts into the page. These scripts can steal session tokens from other users or modify what they see. + +5. **missing-waf** — STRIDE: D (Denial of Service). Without a WAF, automated attacks like SQL injection, XSS, or brute force can reach the app directly and potentially crash it. + +### Trust boundary observation + +Looking at `labs/lab2/output/data-flow-diagram.png`, the arrow "Direct to App (no proxy)" crosses from Internet (User Browser) directly into Container Network (Juice Shop Application), bypassing the Host trust boundary entirely. + +This path is attractive to attackers for several reasons: +1) it uses unencrypted HTTP, so credentials travel in plain text +2) it bypasses the reverse proxy, meaning no security headers are added to responses +3) the direct connection exposes the app's internal port 3000 to the network +4) an attacker on the same network could sniff traffic or perform man-in-the-middle attacks without crossing additional boundaries. + +## Task 2: Secure Variant & Diff + +### Risk count comparison + +| Severity | Baseline | Secure | Δ | +|----------|----------|--------|----| +| elevated | 4 | 2 | -2 | +| low | 5 | 5 | 0 | +| medium | 14 | 13 | -1 | +| Total | 23 | 20 | -3 | + +### Risk categories comparison + +| Category | Baseline | Secure | Δ | +|----------|----------|--------|----| +| unencrypted-communication | 2 | 0 | -2 | +| unencrypted-asset | 2 | 1 | -1 | +| unnecessary-technical-asset | 2 | 2 | 0 | +| unnecessary-data-transfer | 2 | 2 | 0 | +| server-side-request-forgery | 2 | 2 | 0 | +| missing-hardening | 2 | 2 | 0 | +| missing-authentication-second-factor | 2 | 2 | 0 | +| cross-site-request-forgery | 2 | 2 | 0 | +| missing-waf | 1 | 1 | 0 | +| missing-vault | 1 | 1 | 0 | +| missing-identity-store | 1 | 1 | 0 | +| missing-build-infrastructure | 1 | 1 | 0 | +| missing-authentication | 1 | 1 | 0 | +| cross-site-scripting | 1 | 1 | 0 | +| container-baseimage-backdooring | 1 | 1 | 0 | + +### Which risks are gone in the secure variant? + +1. **unencrypted-communication** (2 risks) — fixed by changing HTTP to HTTPS for both the browser-to-app and proxy-to-app connections + +2. **unencrypted-asset** (reduced from 2 risks to 1) — fixed by enabling encryption on Persistent Storage + +### Which risks are still there in the secure variant? + +1. **cross-site-scripting** — still present because HTTPS and encryption do not fix XSS. XSS is a problem in the application code, not in the network layer. Fixing it would require changing how Juice Shop handles user input and output. + +2. **missing-waf** — still present because we did not add a Web Application Firewall to the model. This risk requires deploying an actual WAF in front of the application. + +3. **cross-site-request-forgery** (2 instances remain) — CSRF protection requires implementation-specific anti-CSRF tokens. Our infrastructure changes did not add those. + +### Honesty check + +Total risks dropped from 23 to 20, a reduction of 3 risks. Our changes were simple: switching two connections from HTTP to HTTPS and enabling disk encryption. These changes eliminated the unencrypted-communication category entirely and partially fixed unencrypted-asset. + +What remains are application-layer risks like XSS, CSRF, and missing WAF. These cannot be fixed with configuration changes alone. They require code changes (input validation, output encoding, anti-CSRF tokens) or additional infrastructure (a WAF). + +This shows a typical trade-off in security work. Simple configuration changes give quick wins at low cost. But eliminating the remaining risks would require significantly more effort, including code review, development time, and potentially new infrastructure components. + +