CMPUT-331 · Lesson 12 of 12 · 32 min

Capstone: threat modelling and hardening a web application, ethics and careers

This final lesson brings the course together in one project: securing an invented web application from design to release. You will build a threat model with STRIDE, choose controls from the earlier lessons, score the risk before and after, and run a release checklist. The lesson then turns to the professional side of security: the law around computer access and security research, responsible disclosure, ethics, and the careers open to graduates.

The application is Larch Clinic Booking, an invented online booking system for a small chain of physiotherapy clinics. Patients create accounts, book and cancel appointments, receive text-message reminders and leave notes for their therapist. Staff use an administration portal to manage schedules. The system is a Python web application with a relational database, deployed in containers, built by a continuous integration pipeline, with nightly backups to cloud storage.

Step 1: model the system

Threat modelling starts with a data flow diagram showing processes, data stores, external entities and data flows, with trust boundaries drawn wherever data crosses from a less trusted to a more trusted zone. For Larch Clinic Booking the main boundaries are between the internet and the web tier, between the web tier and the database, between staff devices and the administration portal, and between the build pipeline and production. Each boundary is a place where an attacker's input enters and where controls belong.

A data flow diagram. Patients and staff, external entities on the left, send requests across a dashed internet trust boundary to the web application. The web application reads and writes the database across a second boundary, sends messages to an external SMS provider, and writes logs. A CI pipeline deploys container images to the web application across a third boundary, and backups flow from the database to cloud storage.
Data flow diagram for Larch Clinic Booking with trust boundaries shown as dashed lines

Step 2: enumerate threats with STRIDE

STRIDE, introduced in Lesson 1, prompts for six kinds of threat at each element: spoofing identity, tampering with data, repudiation, information disclosure, denial of service and elevation of privilege. Walking through the diagram element by element produces a list of concrete threats. Each is scored for likelihood and impact on a 1 to 5 scale, then matched with controls from the course, and scored again on the assumption that the controls work. The table in the program below summarizes the result.

The Larch Clinic threat model: STRIDE threats scored before and after controls
# Threat model for "Larch Clinic Booking": STRIDE threats per component, scored before and after controls
threats = [
 # component, STRIDE, threat, likelihood, impact, control, likelihood after
 ("Login",        "S", "credential stuffing with reused passwords", 4, 4, "MFA + breached-password screening + rate limits", 1),
 ("Booking API",  "E", "patient reads another patient's booking by id", 3, 5, "object-level authorization in data layer", 1),
 ("Booking API",  "T", "price or slot altered in the request", 3, 3, "server-side recalculation and validation", 1),
 ("Web pages",    "I", "script injection in appointment notes", 3, 4, "autoescaping templates + strict CSP", 1),
 ("Database",     "I", "backup copied from storage bucket", 2, 5, "encrypted backups, private bucket, key in KMS", 1),
 ("Admin portal", "R", "staff deny changing a record", 3, 2, "append-only audit log with user and time", 1),
 ("Reminder SMS", "D", "SMS endpoint flooded, running up costs", 3, 2, "per-account quotas and anomaly alerts", 2),
 ("CI pipeline",  "T", "compromised dependency in a build", 2, 5, "lock file hashes, SBOM, signed images", 1),
]
print(f"{'component':<13}{'S':<2}{'before':>7}{'after':>6}  control")
before = after = 0
for comp, cat, t, l, imp, ctl, l2 in sorted(threats, key=lambda x: -x[3] * x[4]):
    before += l * imp; after += l2 * imp
    print(f"{comp:<13}{cat:<2}{l * imp:>7}{l2 * imp:>6}  {ctl}")
print(f"total risk score: {before} before controls, {after} after ({100 * (before - after) / before:.0f}% lower)")
Output
component    S  before after  control
Login        S      16     4  MFA + breached-password screening + rate limits
Booking API  E      15     5  object-level authorization in data layer
Web pages    I      12     4  autoescaping templates + strict CSP
Database     I      10     5  encrypted backups, private bucket, key in KMS
CI pipeline  T      10     5  lock file hashes, SBOM, signed images
Booking API  T       9     3  server-side recalculation and validation
Admin portal R       6     2  append-only audit log with user and time
Reminder SMS D       6     4  per-account quotas and anomaly alerts
total risk score: 84 before controls, 32 after (62% lower)

Three observations matter. First, the highest-risk threats are the familiar ones: credential stuffing and broken object-level authorization, which is why Lessons 4, 7 and 9 spent so long on them. Second, controls reduce likelihood far more often than impact; a breach of health records is still severe, which justifies defence in depth rather than relying on any single control. Third, residual risk is never zero. The remaining scores are what the clinic's owners accept, and that decision belongs to them, informed by the analysis, not to the engineers alone.

Step 3: choose and map controls

Step 4: verify before release

A threat model is only useful if its controls are actually in place. Many teams turn the control list into an automated checklist that runs in the build pipeline against configuration and code, so a regression, such as debug mode switched on or a missing header, fails the build. The program below runs such a checklist against Larch Clinic's release configuration. One check fails: administration access is not yet restricted, so the release waits until the portal is placed behind the bastion and identity-aware proxy from Lesson 10.

An automated pre-release security checklist; one control is missing and blocks the release
# A pre-release security checklist evaluated against the project's configuration
config = {
    "tls_min_version": "1.3", "hsts_max_age": 31536000, "csp": "default-src 'self'; object-src 'none'",
    "cookie_flags": {"Secure", "HttpOnly", "SameSite=Lax"}, "password_hash": "argon2id",
    "mfa_for_staff": True, "debug_mode": False, "db_user_privileges": {"SELECT", "INSERT", "UPDATE"},
    "lockfile_hashes": True, "logs_contain_secrets": False, "backup_encrypted": True,
    "container_user": "app", "admin_ip_allowlist": False,
}
checks = [
    ("TLS 1.2 or later",            lambda c: c["tls_min_version"] in ("1.2", "1.3")),
    ("HSTS for at least a year",    lambda c: c["hsts_max_age"] >= 31536000),
    ("CSP present",                 lambda c: bool(c["csp"])),
    ("Session cookie flags",        lambda c: {"Secure", "HttpOnly"} <= c["cookie_flags"]),
    ("Memory-hard password hash",   lambda c: c["password_hash"] in ("argon2id", "scrypt")),
    ("MFA for staff",               lambda c: c["mfa_for_staff"]),
    ("Debug mode off",              lambda c: not c["debug_mode"]),
    ("Least-privilege DB account",  lambda c: "DROP" not in c["db_user_privileges"]),
    ("Pinned dependencies",         lambda c: c["lockfile_hashes"]),
    ("No secrets in logs",          lambda c: not c["logs_contain_secrets"]),
    ("Encrypted backups",           lambda c: c["backup_encrypted"]),
    ("Non-root container",          lambda c: c["container_user"] != "root"),
    ("Admin access restricted",     lambda c: c["admin_ip_allowlist"]),
]
failed = [name for name, test in checks if not test(config)]
for name, test in checks:
    print(f"[{'x' if test(config) else ' '}] {name}")
print(f"{len(checks) - len(failed)}/{len(checks)} passed; to fix before release: {failed}")
Output
[x] TLS 1.2 or later
[x] HSTS for at least a year
[x] CSP present
[x] Session cookie flags
[x] Memory-hard password hash
[x] MFA for staff
[x] Debug mode off
[x] Least-privilege DB account
[x] Pinned dependencies
[x] No secrets in logs
[x] Encrypted backups
[x] Non-root container
[ ] Admin access restricted
12/13 passed; to fix before release: ['Admin access restricted']

Automated checks complement, but do not replace, human review and testing. Before launch the clinic should commission an independent security assessment, including authorized penetration testing of the booking API and administration portal, as described in CISSP-115, and repeat it after major changes. A privacy impact assessment should accompany it, because the system handles health information.

Step 5: plan for incidents

Even a well-designed system can be breached, so the project ends with an incident response plan: who is on call, how to contain an incident (revoking sessions, rotating keys, disabling accounts), how evidence is preserved, when and how to notify patients and regulators, and how lessons learned feed back into the threat model. Regular exercises, even a one-hour tabletop walk-through of a scenario such as a leaked backup, reveal gaps cheaply.

The law and security work

Security professionals work close to legal lines, and good intentions are not a defence. Accessing a computer system without authorization is a crime in both countries, even to look for vulnerabilities, so testing must always rest on explicit written permission that sets the scope, timing and methods.

In Canada, section 342.1 of the Criminal Code makes it an offence to obtain a computer service or intercept a function of a computer fraudulently and without colour of right, and section 430(1.1) covers mischief in relation to computer data. PIPEDA and provincial laws require organizations to protect personal information and report serious breaches. There is no general statutory safe harbour for good-faith security research, so researchers rely on organizations' published vulnerability disclosure policies, and the Government of Canada operates its own disclosure program for federal systems.

Responsible disclosure

When someone finds a vulnerability in a product or service they do not own, coordinated vulnerability disclosure is the accepted practice: report it privately to the vendor, give them reasonable time to fix it, then publish details so that users can protect themselves and others can learn. ISO/IEC 29147 and 30111 describe how vendors should receive and handle reports. A common default is a 90-day deadline, popularized by Google's Project Zero, with extensions when a vendor is acting in good faith. Organizations should publish a vulnerability disclosure policy and a security.txt file (RFC 9116) saying how to report issues and promising not to pursue good-faith researchers; many also run bug bounty programs that pay for valid reports. Published vulnerabilities receive CVE identifiers so everyone can refer to them unambiguously.

A coordinated disclosure timeline built around a 90-day deadline
from datetime import date, timedelta
# A coordinated vulnerability disclosure timeline with a 90-day default deadline
reported = date(2026, 7, 6)
plan = [("report received and acknowledged", 2), ("triage and severity agreed", 10),
        ("fix developed and tested", 45), ("fix released to customers", 60),
        ("advisory and CVE published", 90)]
for step, days in plan:
    print(f"{(reported + timedelta(days=days)).isoformat()}  day {days:>2}: {step}")
print("if no fix by day 90, the reporter may publish after notice, unless an extension is agreed")
Output
2026-07-08  day  2: report received and acknowledged
2026-07-16  day 10: triage and severity agreed
2026-08-20  day 45: fix developed and tested
2026-09-04  day 60: fix released to customers
2026-10-04  day 90: advisory and CVE published
if no fix by day 90, the reporter may publish after notice, unless an extension is agreed

Ethics

Security skills can protect or harm, so the profession has explicit codes of conduct. The ACM Code of Ethics asks computing professionals to avoid harm, respect privacy, honour confidentiality and be honest about the limits of their systems. Security-specific codes, such as those of ISC2 and ISACA, add duties to protect society and act honourably. In practice, ethical security work means staying within authorized scope, minimizing access to personal data during testing, protecting what you find, reporting honestly rather than overstating or hiding risk, and refusing work intended to harm people or to break the law. When ethics and an employer's wishes conflict, professional codes, and in some cases whistleblower protections, support doing the right thing.

In Canada, professional engineering regulators protect the title engineer and require their members to hold paramount the safety and welfare of the public, which includes security failures that could cause harm. CIPS, the Canadian information technology professional association, offers the I.S.P. designation with its own code of ethics.

Careers in security

Security is one of the fastest-growing fields in computing, with persistent shortages of skilled people. Roles span the whole life cycle: security engineers and application security specialists build defences into products; security architects design systems; penetration testers and red teams assess them under authorization; analysts and incident responders in security operations centres detect and handle attacks; cryptographers and researchers study algorithms and vulnerabilities; and governance, risk and privacy specialists connect technology to law and business. Strong foundations in programming, operating systems, networks and cryptography, the subjects of this course and its prerequisites, matter more than any single tool. Many people build experience through capture-the-flag competitions, open-source contributions and bug bounty programs, and later add certifications such as the CISSP that this site's certification track covers.

Worked examples

Worked problem

Prioritizing threats

With limited time before launch, Larch Clinic can implement only three controls this week. Using the threat model, which three should come first?

Show solution
  1. Rank threats by risk score: credential stuffing (16), object-level authorization (15) and script injection in notes (12) are highest.
  2. Choose controls that remove the most risk per unit of effort: object-level authorization checks, MFA with rate limits for logins, and autoescaping templates with a CSP.
  3. Schedule the rest (encrypted backups, audit log, supply chain controls) for the following sprint, and record the accepted interim risk.

Answer: Object-level authorization, MFA with rate limiting, and output encoding with CSP address the three highest risks.

Worked problem

Finding a vulnerability by accident

While using a city recreation-booking website, a student notices that changing a number in the URL shows another family's booking details. What should the student do?

Show solution
  1. Stop immediately: do not try other numbers, download data or test further, because that could be unauthorized access.
  2. Look for the city's vulnerability disclosure policy or security.txt contact; if none exists, contact its privacy or IT office in writing.
  3. Report what was seen, how and when, without sharing anyone's personal data, and allow time for a fix before discussing it publicly.

Answer: Stop, report privately through the disclosure channel with minimal detail, and allow time for a fix.

Worked problem

Scoping an authorized test

A consulting firm is hired to test Larch Clinic Booking. What must the written authorization include?

Show solution
  1. The systems in scope (booking API, admin portal, test environment) and those out of scope (the SMS provider, third-party services, production patient data).
  2. Dates and times, permitted techniques, and limits such as no denial-of-service testing and no access to real patient records.
  3. Contacts for emergencies, how findings are reported and protected, and signatures from someone with authority over every system in scope.

Answer: Scope, exclusions, timing, permitted methods, data-handling rules, contacts and authorized signatures.

Common mistakes

  • MistakeSecurity can be added after the application is built.

    InsteadThreat model during design and verify controls continuously; retrofitting costs far more.

  • MistakeIf controls are in place, residual risk is zero.

    InsteadResidual risk always remains; owners must understand and accept it explicitly.

  • MistakeTesting a website for flaws is fine if you mean well.

    InsteadUnauthorized access is illegal in Canada and the US; get written permission or use a disclosure program.

  • MistakePublishing a vulnerability immediately is the most responsible choice.

    InsteadCoordinate disclosure: report privately and give the vendor reasonable time to fix it.

  • MistakeSecurity careers are only about hacking.

    InsteadMost roles build, operate and govern secure systems; strong fundamentals matter most.

Summary and key terms

  • Threat modelling starts with a data flow diagram and trust boundaries, then applies STRIDE to each element.
  • Score threats before and after controls; residual risk is a decision for system owners.
  • Map controls to threats and verify them with automated checks in the build pipeline.
  • Independent testing, privacy impact assessments and incident response plans complete a secure release.
  • Unauthorized access is a crime in both countries; testing needs written authorization.
  • Coordinated vulnerability disclosure, disclosure policies and security.txt protect users and researchers.
  • Ethical codes and strong technical fundamentals underpin a career in security.
Data flow diagram
A diagram of processes, stores, entities and data flows used in threat modelling.
Trust boundary
A line where data passes between zones of different trust.
STRIDE
Spoofing, tampering, repudiation, information disclosure, denial of service, elevation of privilege.
Residual risk
The risk that remains after controls are applied.
Security checklist
A list of required controls, ideally verified automatically before release.
Penetration test
An authorized assessment that tries to find exploitable weaknesses.
Coordinated vulnerability disclosure
Reporting vulnerabilities privately and publishing after a fix or deadline.
security.txt
A standard file (RFC 9116) telling researchers how to report vulnerabilities.
CVE
Common Vulnerabilities and Exposures: unique identifiers for published vulnerabilities.
Bug bounty
A program that rewards researchers for valid vulnerability reports.

Practice problems

Work each problem on paper first, then reveal the answer to check.

  1. What does a trust boundary mark in a data flow diagram?

    Show answer

    Where data crosses between zones of different trust, a place where controls belong.

  2. Which two threats scored highest in the Larch Clinic model?

    Show answer

    Credential stuffing and patients reading others' bookings by id.

  3. Why do controls usually reduce likelihood rather than impact?

    Show answer

    They make an attack harder, but if it still succeeds the consequences are similar.

  4. Which checklist item blocked the release, and how is it fixed?

    Show answer

    Admin access was not restricted; place the portal behind a bastion or identity-aware proxy.

  5. Name two things a written penetration-test authorization must include.

    Show answer

    Any two of scope, exclusions, timing, permitted methods, data handling, contacts or signatures.

  6. Which Canadian Criminal Code section covers unauthorized use of a computer?

    Show answer

    Section 342.1.

  7. What did the US Department of Justice announce in May 2022?

    Show answer

    That it would not charge good-faith security research under the CFAA.

  8. What is a common default deadline in coordinated disclosure?

    Show answer

    90 days.

  9. What does a security.txt file contain?

    Show answer

    Contact details and policy for reporting vulnerabilities.

  10. Name three security career roles.

    Show answer

    Any three of application security engineer, SOC analyst, penetration tester, cryptography engineer, privacy engineer or architect.

Lesson quiz · 5 questions

Score 60% or more to complete this lesson.

Take the quiz