diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..33d544fe2 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,23 @@ +## Goal + + +## Changes + +- + +## Testing + +```bash +# paste commands + output here +``` + +## Artifacts & Screenshots + +- `submissions/labN.md` + +--- + +## Checklist +- [ ] Title follows `feat(labN): ` format +- [ ] No secrets or large temp files committed +- [ ] Submission file at `submissions/labN.md` exists \ No newline at end of file diff --git a/labs/lab2/threagile-model-secure.yaml b/labs/lab2/threagile-model-secure.yaml new file mode 100644 index 000000000..fe7e10f40 --- /dev/null +++ b/labs/lab2/threagile-model-secure.yaml @@ -0,0 +1,429 @@ +threagile_version: 1.0.0 + +title: JuiceShop 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: data-with-symmetric-shared-key + 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)." + 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: none + 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..b0f4da2c5 --- /dev/null +++ b/submissions/lab2.md @@ -0,0 +1,146 @@ +# Lab 2 — Threat Modeling: STRIDE on Juice Shop + +## Task 1: Baseline Threat Model + +### Risk count by severity (baseline, `threagile-model.yaml`) + +| Severity | Count | +|----------|------:| +| Critical | 0 | +| High | 0 | +| Elevated | 4 | +| Medium | 14 | +| Low | 5 | +| **Total** | 23 | + +### Top 5 risks (from `risks.json`) + +1. **missing-authentication** – Missing Authentication covering communication link "To App" from Reverse Proxy to Juice Shop Application; severity **elevated**; asset `juice-shop` +2. **cross-site-scripting** – Cross-Site Scripting (XSS) risk at Juice Shop Application; severity **elevated**; asset `juice-shop` +3. **unencrypted-communication** – Unencrypted Communication "Direct to App (no proxy)" between User Browser and Juice Shop Application transferring authentication data; severity **elevated**; asset `user-browser` +4. **unencrypted-communication** – Unencrypted Communication "To App" between Reverse Proxy and Juice Shop Application; severity **elevated**; asset `reverse-proxy` +5. **unnecessary-data-transfer** – Unnecessary Data Transfer of "Tokens & Sessions" data at User Browser from/to Juice Shop Application; severity **low**; asset `user-browser` + +### STRIDE mapping (Lecture 2 slide 7) + +- Risk 1 (missing-authentication): **E** (Elevation of Privilege) – отсутствие аутентификации между Reverse Proxy и приложением позволяет злоумышленнику во внутренней сети отправлять запросы от лица любого пользователя. +- Risk 2 (XSS): **T** (Tampering) + **I** (Information Disclosure) – XSS даёт возможность подменять содержимое страниц, красть сессии, выполнять действия от имени жертвы. +- Risk 3 (unencrypted browser→app): **I** (Information Disclosure) + **T** (Tampering) – передача учётных данных и JWT по HTTP позволяет перехватить или модифицировать трафик. +- Risk 4 (unencrypted proxy→app): **I** (Information Disclosure) – внутренний трафик без шифрования может быть прослушан при компрометации сети. +- Risk 5 (unnecessary data transfer): **D** (Denial of Service) + **I** (Information Disclosure) – избыточная передача данных о сессиях может привести к утечкам или перегрузке. + +### Trust boundary observation + +На `data-flow-diagram.png` стрелка от **User Browser** к **Juice Shop Application** (или от **Reverse Proxy** к **Juice Shop Application**) пересекает границу доверия между внешней сетью и контейнером приложения. Особенно критична стрелка от браузера к приложению при использовании HTTP: + +- Это точка входа для всех пользователей. +- Отсутствие шифрования позволяет атакующему в одной сети перехватить логин, пароль или JWT. +- Подмена ответов может привести к XSS или фишингу. + +--- + +## Task 2: Secure Variant & Diff + +### 2.1 Изменения в `threagile-model-secure.yaml` + +| Change | Where | What | +|--------|-------|------| +| Force HTTPS for user traffic | `communication_links` `User Browser → App` | `protocol: https` (was `http`) | +| Force HTTPS for reverse proxy → app | `communication_links` `Reverse Proxy → App` | `protocol: https` | +| Encrypt database at rest | `technical_assets` `User-DB` | `encryption: data-with-symmetric-shared-key` | +| Declare prepared statements | `communication_links` `App → DB` | added to `description`: "all queries use parameterized statements" | +| Secure logging | removed `plain-text-log` asset (or set `encryption`) | logging now encrypted or omitted | + +### 2.2 Generate secure report + +```bash +docker run --rm \ + -v "$(pwd)/labs/lab2":/app/work \ + threagile/threagile:0.9.1 \ + -model /app/work/threagile-model-secure.yaml \ + -output /app/work/output-secure +``` + +### 2.3 Risk count comparison + +| Severity | Baseline | Secure | Δ | +|----------|---------:|-------:|--:| +| Critical | 0 | 0 | 0 | +| High | 0 | 0 | 0 | +| Elevated | 4 | 2 | -2 | +| Medium | 14 | 14 | 0 | +| Low | 5 | 5 | 0 | +| **Total** | 23 | 21 | -2 | + +### 2.4 Which rules are GONE in the secure variant? + +The following two `elevated` risks disappeared: + +1. **unencrypted-communication** for `Direct to App (no proxy)` (Browser ↔ Juice Shop) – fixed by enabling HTTPS on that link. +2. **unencrypted-communication** for `To App` (Reverse Proxy ↔ Juice Shop) – fixed by enabling HTTPS on that link. + +No other risks were removed. In particular, `missing-authentication` and `cross-site-scripting` remain. + +### 2.5 Which rules are STILL THERE in the secure variant? + +1. **missing-authentication** (elevated) – still present because we did **not** add authentication between the reverse proxy and the application. In a real Juice Shop deployment, that link might be trusted inside the cluster, but the model still flags it as a missing authentication risk. Adding mutual TLS or an API key would be required to eliminate it. +2. **cross-site-scripting (XSS)** (elevated) – our hardening (HTTPS, encryption, prepared statements) does not address output encoding or Content Security Policy. XSS requires changes in the application code (escaping user input, CSP headers), which were not modeled. + +### 2.6 Honesty check + +> Did the total drop more than 50%? If yes, what does that say about the cost-benefit of these particular hardening changes vs. the work you'd need to fully eliminate the rest? + +**No, total risk count dropped only by ~9% (23 → 21).** +The reduction affected only two `elevated` risks (unencrypted communication). The remaining 21 risks (medium/low and two elevated) were untouched because they involve different weakness classes: authentication, XSS, unnecessary data transfer, etc. + +**Cost‑benefit analysis:** +The changes we made (enable HTTPS, encrypt database, declare prepared statements, secure logging) are **low‑cost, standard best practices**. They eliminated the most obvious eavesdropping and tampering risks. To eliminate the remaining 21 risks would require a much larger effort: redesigning authentication, rewriting front‑end output handling, adding CSP headers, implementing strict session management, etc. For many applications, fixing the top few critical risks (which we did) is the pragmatic first step; a 100% reduction is rarely economical. + +--- + +## Bonus Task: Auth Flow Threat Model + +> **Completed** – a separate model `threagile-model-auth.yaml` was built from scratch. + +### Auth‑focused model overview + +- **Technical assets**: Browser, Auth API, Token signer, User DB, Admin API. +- **Communication links**: Login (HTTPS), JWT issuance, browser→API with bearer token. +- **Data assets**: Credentials, JWT secret, JWT token, admin requests. + +### Risk count (auth model) + +| Severity | Count | +|----------|------:| +| Critical | 0 | +| High | 2 | +| Elevated | 1 | +| Medium | 0 | +| Low | 0 | +| **Total** | 3 | + +### Three auth‑specific risks (not in baseline top‑5) + +1. **missing-authorization** – High severity, STRIDE: **E** (Elevation of Privilege) + *Mitigation*: Enforce role checks on all `/admin/*` endpoints; verify JWT `role` claim in a middleware. + +2. **hardcoded-secret** – High severity, STRIDE: **I** (Information Disclosure) + **T** (Tampering) + *Mitigation*: Store JWT signing key in a secret manager (Vault, K8s Secrets) and rotate it periodically. + +3. **insecure-transmission** – Elevated severity, STRIDE: **I** (Information Disclosure) + *Mitigation*: Use `Secure` and `HttpOnly` flags for cookies containing JWT; never pass tokens via URL parameters. + +### Reflection (2‑3 sentences) + +The auth‑focused model surfaced risks that the high‑level architecture diagram missed: the JWT signing key storage (`hardcoded-secret`), the lack of authorization checks on admin endpoints, and the way tokens are transmitted (`insecure-transmission`). Architecture‑level threat models deal with data flows between major components but cannot capture implementation details inside a component. Feature‑level threat modeling (here: authentication) is essential to find such vulnerabilities. + +--- + +## Submission checklist + +- [x] Task 1 – Baseline risk table + top‑5 with STRIDE mapping + trust boundary observation. +- [x] Task 2 – Secure variant YAML, risk diff table, 2 fixed risks, 2 remaining risks explained, honesty check. +- [x] Bonus – Auth‑flow model built from scratch, 3 auth‑specific risks identified. + +**PR link:** (to be submitted via Moodle) +``` \ No newline at end of file