Threat Modeling New Features

Master threat modeling for new features in this Secure development tutorial. Learn the core concept, step-by-step application, hands-on walkthrough, and troubleshooting tips to integrate security early.

Focus: use threat modeling for new features

Sponsored

You've just pushed a new feature — maybe a file upload endpoint, a password reset flow, or an admin-only analytics dashboard. The code works, tests pass, and the pull request is approved. But did anyone ask: what happens if a malicious user gets their hands on this? That question is the heart of threat modeling. Without it, you're shipping features with unknown security blind spots, and the cost of finding those blind spots later — in a breach, a compliance fine, or a firefight at 3 AM — is far higher than the cost of thinking it through now. In this lesson, you'll learn how to use threat modeling for new features so you can find and fix security issues before they reach production, not after.

The problem this lesson solves

When you add a new feature, the most common security mistakes aren't exotic zero-days. They're predictable flaws: an unauthenticated endpoint, an over-permissive API call, a file upload that accepts executable code, or a log that leaks customer PII. Why do these keep happening? Because most developers are reactive — they wait for a security review, a pentest, or a breach to surface an issue. By that time, the design is baked in, changing it is expensive, and your users' data may already be at risk.

The solution is to shift security left — to consider threats as you design the feature, not after it's built. Threat modeling is a structured way to do exactly that. It forces you to answer four questions before writing code: What are we building? What can go wrong? How do we prevent it? And how do we recover if it happens anyway? Without this step, you're not just building a feature; you're building an attack surface.

Core concept / mental model

Think of threat modeling like planning a bank's floor plan before pouring the concrete. You wouldn't put the vault door in the lobby or leave the back entrance unlocked just because the architect forgot to ask. Similarly, you don't ship a file upload feature without asking: who's allowed to upload? What file types? What happens if someone uploads a script with a .php extension? Threat modeling is that architectural planning for security.

What is threat modeling?

Threat modeling is a systematic process of identifying, analyzing, and mitigating potential security threats to a system or feature. It's not a one-time activity — it's a habit you bake into your development workflow.

  • Asset — something valuable that attackers want: user data, credentials, money, availability.
  • Threat — anything that can harm an asset: unauthorized access, data theft, denial of service.
  • Attack vector — the path an attacker uses to exploit a threat: a network request, a crafted file, a SQL injection payload.
  • Mitigation — a control that reduces or eliminates the threat: authentication, input validation, rate limiting, encryption.

Pro tip: You don't need to be a security expert to start threat modeling. You need curiosity and a willingness to ask "what if?" about the feature you're building.

The core mental model: the 4-step loop

  1. Decompose — break the feature into its components: entry points, data flows, trust boundaries.
  2. Identify — for each component, ask what threats could target it.
  3. Mitigate — design controls to stop or reduce the impact of those threats.
  4. Review — check that your mitigations cover the threats, and iterate as the feature changes.

This loop is iterative. As you add a new dependency or change a data flow, you re-run it.

How it works step by step

Let's walk through a concrete example you'll use in the hands-on section: an event registration feature where a user signs up for a webinar.

Step 1: Decompose the feature

Draw a simple diagram (in words or on paper) of the feature's data flow:

  • Entry point: HTTP POST /api/register
  • Data: user email, name, event ID
  • Backend: Python Flask app
  • Database: stores registrations
  • Third party: email confirmation service

Step 2: Identify threats

Use a structured list of common threat categories to brainstorm. A widely used framework is STRIDE, which stands for:

Letter Threat Question to ask
S Spoofing Can someone impersonate another user?
T Tampering Can someone modify data in transit or at rest?
R Repudiation Can a user deny doing something without evidence?
I Information disclosure Can sensitive data leak to unauthorized parties?
D Denial of service Can the feature be overwhelmed or crashed?
E Elevation of privilege Can a normal user gain admin rights?

For the registration feature, you might identify:

  • Spoofing: someone registers with someone else's email.
  • Tampering: a bot floods registrations with fake data.
  • Information disclosure: the API returns other users' email addresses.
  • Denial of service: the endpoint is open to unlimited requests.

Step 3: Plan mitigations

For each threat, define a concrete mitigation:

  • Spoofing: add email verification (one-time link).
  • Tampering: use a CAPTCHA or rate limit.
  • Information disclosure: only return the current user's registration.
  • Denial of service: apply rate limiting and input validation.

Step 4: Review and iterate

Document your model, share it with your team, and revisit it whenever the feature changes (e.g., adding a roles field or an admin panel).

Hands-on walkthrough

Let's build a small Flask app with a registration endpoint, apply threat modeling, and then fix the issues we identify.

Setup

Create a virtual environment and install Flask:

python -m venv venv
source venv/bin/activate
pip install flask

The vulnerable version (before threat modeling)

# app.py
from flask import Flask, request, jsonify

app = Flask(__name__)
registrations = []

@app.route('/api/register', methods=['POST'])
def register():
    data = request.get_json()
    email = data['email']
    name = data['name']
    event_id = data['event_id']
    registrations.append({'email': email, 'name': name, 'event_id': event_id})
    return jsonify({'status': 'registered'}), 201

if __name__ == '__main__':
    app.run(debug=True)

This endpoint has multiple problems:

  • No input validationdata['email'] will raise a KeyError if missing.
  • No rate limiting — an attacker can loop millions of requests.
  • No authentication — anyone can register for anything, and the endpoint accepts any payload.

Apply threat modeling: what would you fix?

Think in STRIDE terms. The biggest issue is denial of service and tampering. Let's add basic mitigations.

The hardened version (after threat modeling)

from flask import Flask, request, jsonify, abort
from functools import wraps
import re
import time
from collections import defaultdict

app = Flask(__name__)
registrations = []
rate_limit_store = defaultdict(list)

# Simple rate limiter: max 5 requests per minute per IP
def rate_limit(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        ip = request.remote_addr
        now = time.time()
        # Remove old timestamps (>60s)
        rate_limit_store[ip] = [t for t in rate_limit_store[ip] if now - t < 60]
        if len(rate_limit_store[ip]) >= 5:
            abort(429, description="Too many requests")
        rate_limit_store[ip].append(now)
        return f(*args, **kwargs)
    return wrapper

def validate_email(email: str) -> bool:
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return re.match(pattern, email) is not None

@app.route('/api/register', methods=['POST'])
@rate_limit
def register():
    data = request.get_json(silent=True)
    if not data:
        return jsonify({'error': 'Invalid JSON'}), 400

    email = data.get('email')
    name = data.get('name')
    event_id = data.get('event_id')

    # Validate inputs
    if not email or not validate_email(email):
        return jsonify({'error': 'Valid email required'}), 400
    if not name or len(name) > 100:
        return jsonify({'error': 'Name must be 1-100 characters'}), 400
    if not event_id:
        return jsonify({'error': 'Event ID required'}), 400

    # Mitigate spoofing: store a pending registration, send a confirmation email
    # (simplified here — we'll just append after "email verification")
    registrations.append({'email': email, 'name': name, 'event_id': event_id})
    return jsonify({'status': 'pending_verification'}), 201

Now run the server and test with curl:

curl -X POST http://127.0.0.1:5000/api/register \
  -H "Content-Type: application/json" \
  -d '{"email": "alice@example.com", "name": "Alice", "event_id": 1}'
\# Expected: {"status": "pending_verification"}

Test an invalid email:

curl -X POST http://127.0.0.1:5000/api/register \
  -H "Content-Type: application/json" \
  -d '{"email": "not-an-email", "name": "Bob", "event_id": 2}'
\# Expected: {"error": "Valid email required"}

This is just a taste — real implementations add authentication, database persistence, and email verification.

Pro tip: Use automated tools like OWASP Dependency-Check and bandit to scan your code for common issues, but remember they don't replace human threat modeling — they're the linting, not the architecture.

Compare options / when to choose what

There are several structured threat modeling approaches. Which one should you use?

Approach Best for Focus Complexity
STRIDE General software threats Categorizes threats (spoofing, etc.) Low
DREAD Risk assessment priorization Rates threats by damage, etc. Medium
PASTA Multi-stage, business-focused Aligns security with business goals High
Attack trees Modeling a specific attack path Identifies branching attack vectors Medium
Misuse cases Requirements gathering Extends use cases with negative paths Low
  • STRIDE is great for a quick, options-focused review — you'll use it most days.
  • DREAD helps you prioritize which threats to fix first, but its scoring can be subjective.
  • PASTA is enterprise-grade but heavy; it's overkill for a small feature.
  • Attack trees are useful when you want to map out how an attacker could reach a crown-jewel asset.
  • Misuse cases are helpful when you're still in requirements meetings.

Troubleshooting & edge cases

Even with a model in place, things go wrong. Here's how to handle common pitfalls:

  • Problem: You identified too many threats and feel overwhelmed. Solution: Prioritize with DREAD or the Top 10 risks from OWASP. Fix high-impact, high-probability items first.
  • Problem: Your feature is "micro" — just a small endpoint. Solution: Don't skip threat modeling entirely — do a 5-minute version with STRIDE. Even small features surface data.
  • Problem: The threat model becomes outdated after a change. Solution: Make threat modeling part of the PR review checklist. If a PR changes data flows or trust boundaries, update the model.
  • Problem: Your team lacks security expertise. Solution: Use guides like OWASP ASVS and the Threat Modeling Manifesto. Pair developers with security champions.
  • Edge case: Third-party dependencies introduce new threats. Solution: Run pip-audit or npm audit regularly, and include supply-chain threats in your model.

What you learned & what's next

You've learned that threat modeling for new features is a structured way to find security problems early. You can now explain the core idea — decompose, identify, mitigate, review — and you've applied it to a real Flask endpoint, fixing input validation, rate limiting, and error handling. You also know how to compare methods like STRIDE vs. PASTA and troubleshoot common issues.

The next step in the Secure development track is input validation posture, where you'll dive deeper into checking and sanitizing every piece of data that enters your system. Threat modeling tells you where to validate; the next lesson will show you how to do it rigorously.

Practice recap

Take a feature you're currently building (or a feature from a past project) and run a 15-minute threat modeling session using the STRIDE framework. Write down the entry points, data flows, and at least one threat per STRIDE category, then list the mitigations you would implement. Share your findings with a teammate and discuss what you might have missed.

Common mistakes

  • Skipping threat modeling because the feature is 'small' — small endpoints can still expose sensitive data or be abused as attack vectors.
  • Only focusing on the happy path — forgetting to model what happens when an attacker sends malformed input or excessive requests.
  • Treating threat modeling as a one-time activity at the start: not updating the model when the feature changes, leading to stale security assumptions.
  • Over-engineering the mitigation: adding hundreds of lines of security code without first identifying the actual threats, making the feature harder to maintain.
  • Not involving the whole team — threat modeling is collaborative; a solo developer often misses attack vectors that a colleague would catch.

Variations

  1. Use the DREAD model when you need to prioritize which threats to address based on damage, reachability, and exploitability.
  2. Adopt the PASTA approach for enterprise-scale projects where security must align with business goals and involve multiple stakeholders.
  3. For agile teams, incorporate a lightweight threat modeling session into each sprint's planning — using a simple checklist rather than a formal document.

Real-world use cases

  • An e-commerce startup adds a coupon redemption endpoint — threat modeling reveals abuse via code guessing, leading to rate limiting and validation.
  • A healthcare app introduces a patient data export feature — threat modeling identifies the risk of unauthorized access, prompting stricter authentication and authorization.
  • A SaaS platform rolls out a webhook notification system — threat modeling uncovers potential SSRF attacks, guiding the team to whitelist outbound IPs and validate payloads.

Key takeaways

  • Threat modeling is a proactive security practice that helps you find and fix vulnerabilities before deployment.
  • Follow the four-step loop: decompose, identify, mitigate, and review.
  • Use STRIDE to systematically categorize threats across your feature's components.
  • Apply threat modeling to every feature, no matter how small — and update it as the feature evolves.
  • Prioritize mitigations based on risk; not every threat needs an elaborate solution.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.