Skip to content
Thinking Like an Attacker: A Practical Guide to Threat Modeling for .NET Architects with STRIDE

Thinking Like an Attacker: A Practical Guide to Threat Modeling for .NET Architects with STRIDE

1 Introduction: The Imperative of Proactive Security

Security has moved from being a specialized niche to a fundamental part of software architecture. Modern systems operate in a permanently hostile environment where automated bots and targeted attackers continuously scan for weaknesses.

For .NET architects, this means security is no longer something that can be delegated or postponed—it must be embedded into the earliest design decisions. The way you define trust boundaries, select frameworks, and structure services will determine how well your application withstands real-world threats.

1.1 The “Shift Left” Mentality

Shifting left means integrating security early in the software development lifecycle (SDLC). Rather than placing security checks only at the end—during penetration testing or pre-release review—you introduce them in requirements gathering, architecture design, and early development.

This approach changes the dynamics of both cost and risk. Early integration allows you to:

  • Identify vulnerabilities before they’re deeply embedded in the system
  • Avoid costly architectural redesigns
  • Create a security-aware development culture

1.1.1 Moving Beyond “Bolting On” Security at the End of the Lifecycle

Historically, many projects followed a waterfall model where security was handled after functional requirements were complete. This often led to “security patchwork,” where controls were applied awkwardly to fit an already rigid structure.

Example: Consider an ASP.NET Core MVC application that initially had no requirement for multi-factor authentication (MFA). Adding MFA after the system is live might require:

  • Redesigning the login workflow
  • Modifying Identity providers
  • Rebuilding user session handling
  • Retesting all authentication flows

In contrast, designing with MFA in mind from the start means:

  • The architecture naturally supports multiple authentication factors
  • The data model accommodates related attributes (e.g., phone number, MFA secret)
  • User flows are tested early with security included

Retrofitting controls is not just inconvenient—it can introduce new vulnerabilities by forcing rushed changes under production deadlines.

1.1.2 The Economics of Security: The Exponential Cost of Fixing Flaws in Production

Industry studies consistently show a dramatic cost escalation the later an issue is found. For security flaws, the impact is often worse than for functional bugs because they:

  • Can result in active data breaches before discovery
  • Often require urgent, disruptive fixes
  • Trigger compliance violations (GDPR, HIPAA, PCI DSS)
  • Damage customer trust and brand reputation

Illustration: If a SQL injection vulnerability is found:

  • During code review: Fix might take an hour of developer time.
  • During functional testing: Fix may require changes to multiple stored procedures, retesting, and redeployment.
  • After a breach: Fix must be rushed, incident response triggered, forensic investigation conducted, legal counsel engaged, and possibly customer notification sent—costing hundreds of thousands of dollars.

For .NET architects, shift left is about reducing these risks by ensuring that security assumptions are tested in the architecture phase, long before code reaches production.

1.2 The Architect’s Mandate in a Hostile Digital World

The role of the architect is no longer limited to performance tuning, scalability, and maintainability. In 2025, a competent architect is also a security strategist.

Attackers no longer need to be highly skilled individuals—they can be automated scripts scanning millions of endpoints daily. That means even small architectural oversights—like an open management port or an unvalidated API parameter—can be exploited in hours.

1.2.1 Why Security is a Foundational Architectural Concern, Not an Afterthought

Architecture defines:

  • Trust Boundaries: Where sensitive operations occur and who can initiate them
  • Data Flow: How information moves between components, services, and external parties
  • Technology Choices: Which frameworks, protocols, and storage mechanisms are used
  • Integration Points: How your system communicates with third-party services

Security flaws at this level are often systemic. If you design an internal API without authentication because “it’s only used inside the network,” you’ve built in a permanent vulnerability—one that can be exploited if an attacker breaches any single internal component.

Example: A .NET Core microservice exposing a management API internally may work fine—until a compromised internal service starts sending malicious configuration commands. Without authentication at the architectural level, the vulnerability exists no matter how secure the code itself is.

1.2.2 How Secure Design Principles Lead to More Resilient and Robust Systems

Adopting secure design principles strengthens both security posture and operational resilience.

Key principles for .NET architects include:

  1. Least Privilege – Services, processes, and users should have only the permissions they need. In Azure, this might mean using Managed Identities with narrowly scoped role assignments.
  2. Defense in Depth – Multiple layers of security, such as TLS for transport, signed JWTs for authentication, and API Gateway rate limiting.
  3. Fail Securely – If something fails, it should fail in a secure state. For example, a payment processing service that can’t reach the authorization provider should reject transactions rather than approve them.
  4. Secure Defaults – Make the safest configuration the default, such as enabling HTTPS redirection by default in ASP.NET Core.

These principles lead to systems that contain failures, resist attack escalation, and maintain availability even under duress.


2 Threat Modeling Fundamentals

Threat modeling is the structured practice of identifying, prioritizing, and addressing threats to a system’s security. For architects, it is a way to validate designs before they’re implemented and ensure that risks are managed consistently.

2.1 Defining Threat Modeling

2.1.1 A Structured Process to Identify, Quantify, and Address Potential Security Threats

Threat modeling is not an ad-hoc brainstorming session. It follows a repeatable process that:

  1. Creates an accurate model of the system’s components, data flows, and trust boundaries
  2. Identifies potential threats based on known attack categories
  3. Prioritizes risks using impact and likelihood assessments
  4. Designs mitigations that can be implemented as part of development

In practice for .NET architects: You might begin with a Data Flow Diagram (DFD) showing how an ASP.NET Core API interacts with a SQL Server database, Azure Key Vault, and an external payment service. You then evaluate each connection for confidentiality, integrity, and availability risks.

2.1.2 Answering the Four Core Questions

2.1.2.1 What Are We Building?

This is about clarity. A shared understanding of the architecture ensures that everyone is modeling the same system. Include:

  • Component diagrams (services, APIs, databases)
  • Technology stack (e.g., ASP.NET Core 8, Azure Functions, Redis)
  • External integrations (e.g., payment gateways, identity providers)
  • Data classification (sensitive PII, financial data, public content)
2.1.2.2 What Can Go Wrong?

Think like an attacker. Consider:

  • Could an external user impersonate another user?
  • Could someone modify data in transit?
  • Could sensitive logs be exposed via a misconfigured endpoint?
  • Could a single service outage cascade into a total failure?
2.1.2.3 What Are We Going to Do About It?

Define concrete mitigations:

  • Authentication: Use Duende IdentityServer with OpenID Connect and short-lived JWTs
  • Data Integrity: Sign messages with HMAC using Azure Key Vault-stored keys
  • Availability: Implement Polly-based circuit breakers on outbound calls
  • Confidentiality: Apply Always Encrypted on SQL Server columns containing PII
2.1.2.4 Did We Do a Good Enough Job?

Verification is essential. Perform:

  • Peer review of the threat model
  • Security-focused unit and integration tests
  • Static Application Security Testing (SAST) in the CI/CD pipeline
  • Periodic red-team exercises

2.2 Introducing the STRIDE Methodology

2.2.1 A Brief History: From Microsoft’s Secure Development Lifecycle to Industry Standard

Microsoft developed STRIDE in the early 2000s as part of its Secure Development Lifecycle to help teams systematically identify threats. The six categories correspond to key security properties:

  • Spoofing – Authentication failures
  • Tampering – Integrity violations
  • Repudiation – Non-repudiation failures
  • Information Disclosure – Confidentiality failures
  • Denial of Service – Availability failures
  • Elevation of Privilege – Authorization failures

While originally internal to Microsoft, STRIDE has been widely adopted by the security community because of its simplicity and broad applicability.

2.2.2 Why STRIDE is an Ideal Framework for Development and Architecture Teams

For .NET architects, STRIDE works because it:

  • Is easy to remember – Six categories cover most threat scenarios
  • Maps directly to DFD elements – Spoofing applies to external entities, Tampering to data flows, etc.
  • Works at multiple levels – From individual services to entire distributed systems
  • Encourages completeness – Prevents overlooking a threat class by focusing only on “common” risks

By integrating STRIDE into architecture reviews, you ensure that every trust boundary, data store, and process is considered through an attacker’s lens.


3 System Decomposition: Creating Your Security Blueprint

Before you can protect a system, you must first understand it at a structural level. System decomposition is the process of breaking down your application into identifiable parts—components, services, data stores, and communication channels—so you can examine each for potential vulnerabilities.

Without this clarity, threat modeling becomes guesswork. With it, you gain a security blueprint that will guide every decision in your architecture and implementation.

3.1 The First Principle: You Can’t Secure What You Don’t Understand

It’s impossible to defend what you cannot visualize or define. Security architects need a complete, accurate, and up-to-date picture of the system.

For a .NET architect, this often means mapping:

  • All application layers (UI, API, services, data persistence)
  • All communication flows (HTTP, gRPC, message queues)
  • All external dependencies (payment providers, identity services, third-party APIs)
  • All data classifications (public, internal, confidential, sensitive PII)

An incomplete model will almost always miss at least one high-risk exposure point—often at the weakest link, such as an internal admin interface or an overlooked background service.

3.2 The Power of Data Flow Diagrams (DFDs)

Data Flow Diagrams are the most widely used tool for system decomposition in threat modeling. They allow you to visualize how information moves through your application and where attackers might attempt to interfere.

A DFD is not a UML diagram or an architecture diagram for developers. It is a security-focused abstraction that captures entities, processes, data stores, and flows in a way that supports STRIDE analysis.

3.2.1 Core DFD Elements and Notation

A security-focused DFD for threat modeling contains four core elements:

3.2.1.1 External Entities (Users, External Systems)

External entities are anything outside your control that interacts with the system. These may be:

  • End users accessing your .NET web application
  • Third-party services providing APIs or data feeds
  • External payment processors like Stripe or PayPal
  • Cloud-based identity providers such as Azure AD B2C

In a DFD, external entities are usually represented as rectangles. The key point is that they exist outside your trust boundaries, so all data flows to or from them must be evaluated for potential threats.

3.2.1.2 Processes (Services, Functions, Application Layers)

Processes represent the parts of your system that handle, transform, or route data. In a .NET architecture, these could include:

  • An ASP.NET Core API processing incoming requests
  • A background worker using .NET Hosted Services to process messages from Azure Service Bus
  • A Razor Pages front-end rendering dynamic content
  • A microservice responsible for order fulfillment in a commerce platform

In DFDs, processes are usually circles or ovals. Each process must be assessed for spoofing, tampering, and elevation of privilege risks.

3.2.1.3 Data Stores (Databases, Caches, File Systems)

Data stores persist information for later retrieval. Common .NET examples include:

  • SQL Server databases using Entity Framework Core
  • Redis caches for session or distributed data storage
  • Azure Blob Storage for media files
  • Local file systems used in hybrid deployments

In a DFD, data stores are drawn as open-ended rectangles. Every data store must be evaluated for confidentiality (encryption at rest), integrity (protection from tampering), and access controls.

3.2.1.4 Data Flows (API Calls, Event Streams, Data Transfer)

Data flows connect processes, external entities, and data stores. These flows could be:

  • HTTPS calls from a Blazor WebAssembly app to an ASP.NET Core API
  • gRPC calls between microservices in Kubernetes
  • Messages sent via RabbitMQ or Azure Service Bus
  • File uploads from clients to Azure Blob Storage

In DFDs, data flows are arrows indicating the direction of transfer. Every flow must be evaluated for tampering, information disclosure, and denial-of-service threats.

3.2.2 Establishing Trust Boundaries: The Most Critical DFD Concept

Trust boundaries are arguably the single most important concept in threat modeling. A trust boundary is a point where the level of trust changes—usually between different security domains.

When a flow crosses a trust boundary, you must verify the integrity, authenticity, and confidentiality of that data.

3.2.2.1 Defining Zones of Trust

Common zones in a .NET application might include:

  • Public Internet – No trust. Any data from here must be treated as hostile until proven otherwise.
  • DMZ (Demilitarized Zone) – Semi-trusted; typically hosts an API gateway or reverse proxy.
  • Internal Virtual Network (VNet) – More trusted, but still requires internal service authentication.
  • Corporate Network – Highest trust, usually containing internal administration tools and sensitive services.
3.2.2.2 Visualizing Where and Why Security Controls are Necessary

Once trust boundaries are drawn, you can quickly see:

  • Where authentication is required (e.g., API Gateway to internal services)
  • Where encryption in transit is mandatory (e.g., DMZ to database)
  • Where rate limiting or input validation should be enforced

If you skip this visualization, it’s easy to overlook internal flows that attackers can exploit after gaining a foothold in one zone.

3.3 Initial DFD Walkthrough: A Monolithic .NET Web App

Even in a monolithic architecture, DFDs are essential. Let’s take a traditional ASP.NET Core MVC application backed by a SQL Server database.

3.3.1 A Step-by-Step Guide to Diagramming

  1. Identify external entities:

    • End user (browser)
    • Admin user (internal staff)
  2. Identify processes:

    • Web application server (ASP.NET Core MVC app)
  3. Identify data stores:

    • SQL Server database storing customer profiles, orders, and payment history
  4. Identify data flows:

    • HTTPS requests from users to web server
    • SQL queries from app to database
    • HTML responses back to browser
  5. Draw trust boundaries:

    • Between Public Internet and web server
    • Between web server and SQL database (often in an internal network zone)

With this diagram, you can now apply STRIDE to each component and flow.


4 A Deep Dive into the STRIDE Framework

The STRIDE methodology is more than just a checklist—it’s a structured way to think like an attacker and ensure that your architecture accounts for all major classes of threats. Each category maps to specific security properties and is best understood through real-world scenarios and mitigation strategies.

4.1 Understanding the Six Threat Categories

4.1.1 S – Spoofing (Threat to Authentication)

4.1.1.1 What It Is

Spoofing is the act of illegitimately assuming the identity of another user, service, or system component. It directly threatens authentication, undermining trust in the system’s identity model.

In a distributed .NET application, spoofing can occur:

  • At the user level: A malicious actor uses stolen credentials to log in.
  • At the service level: A rogue service impersonates another microservice to access sensitive APIs.
  • At the infrastructure level: An attacker manipulates DNS records to redirect requests.
4.1.1.2 Examples
  1. Stolen Credentials – If your ASP.NET Core API accepts a username/password without MFA, leaked credentials from another site breach can allow attackers direct access.
  2. Forged JWTs – If your services trust unsigned or improperly validated JSON Web Tokens, an attacker can forge tokens granting themselves admin privileges.
  3. DNS Spoofing – Manipulating DNS resolution to direct requests to an attacker-controlled endpoint.
.NET Mitigation Example: Validating JWT Tokens

In ASP.NET Core, always validate token signatures and issuers when using JWT authentication:

services.AddAuthentication("Bearer")
    .AddJwtBearer("Bearer", options =>
    {
        options.Authority = "https://identity.mycompany.com";
        options.RequireHttpsMetadata = true;
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = "https://identity.mycompany.com",
            ValidateAudience = true,
            ValidAudience = "api_gateway",
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero
        };
    });

Key Takeaways:

  • Always verify signatures.
  • Use short-lived tokens.
  • Implement MFA for high-value accounts.

4.1.2 T – Tampering (Threat to Integrity)

4.1.2.1 What It Is

Tampering occurs when an attacker modifies data—either in transit or at rest—without authorization. This compromises the system’s integrity, leading to incorrect, misleading, or malicious outcomes.

4.1.2.2 Examples
  1. Price Manipulation – Modifying JSON payload in a PUT request to lower the price of an item before checkout.
  2. Log Alteration – Deleting or altering log entries to hide traces of an intrusion.
  3. SQL Injection – Modifying database commands to change or delete data.
.NET Mitigation Example: Message Signing for Integrity

When sending messages between microservices over RabbitMQ or Azure Service Bus, you can sign payloads with HMAC to ensure they are not modified in transit:

public static string SignMessage(string payload, string secretKey)
{
    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secretKey));
    var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
    return Convert.ToBase64String(hash);
}

public static bool VerifySignature(string payload, string signature, string secretKey)
{
    var expectedSignature = SignMessage(payload, secretKey);
    return expectedSignature == signature;
}

Key Takeaways:

  • Use HTTPS for transport encryption.
  • Sign critical messages to detect tampering.
  • Implement parameterized queries to prevent SQL injection.

4.1.3 R – Repudiation (Threat to Non-Repudiation)

4.1.3.1 What It Is

Repudiation happens when a user denies performing an action and the system cannot prove otherwise. This typically results from inadequate logging or lack of secure audit trails.

4.1.3.2 Examples
  1. Transaction Denial – A user claims they never made a bank transfer, but no signed or timestamped log exists.
  2. Admin Action Denial – A system administrator deletes user accounts but denies the action, and the logs are incomplete.
.NET Mitigation Example: Secure Audit Logging with Serilog
Log.Logger = new LoggerConfiguration()
    .Enrich.WithProperty("Application", "OrderingService")
    .Enrich.WithProperty("Environment", "Production")
    .WriteTo.File("logs/audit-.log", rollingInterval: RollingInterval.Day)
    .WriteTo.Seq("http://seq-server")
    .CreateLogger();

// Example usage
Log.Information("Order {OrderId} created by {UserId}", orderId, userId);

Best Practices:

  • Use append-only logging stores.
  • Timestamp all entries and sync clocks (NTP).
  • Store logs in a separate, restricted system.

4.1.4 I – Information Disclosure (Threat to Confidentiality)

4.1.4.1 What It Is

Information disclosure involves exposing sensitive information to unauthorized parties. This can occur via insecure APIs, verbose error messages, or unencrypted data.

4.1.4.2 Examples
  1. Leaking PII in Errors – Returning detailed stack traces in production containing customer data.
  2. Insecure Direct Object Reference (IDOR) – Allowing users to access other users’ invoices by changing an ID in the URL.
  3. Unencrypted Data Stores – Storing PII in plain text in SQL Server.
.NET Mitigation Example: Data Encryption at Rest with EF Core and Always Encrypted
-- Enable Always Encrypted for a column
CREATE COLUMN MASTER KEY MyCMK
WITH (
    KEY_STORE_PROVIDER_NAME = 'AZURE_KEY_VAULT',
    KEY_PATH = 'https://mykeyvault.vault.azure.net/keys/mycmk/'
);

CREATE COLUMN ENCRYPTION KEY MyCEK
WITH VALUES (
    COLUMN_MASTER_KEY = MyCMK,
    ALGORITHM = 'RSA_OAEP',
    ENCRYPTED_VALUE = ...
);

Key Takeaways:

  • Use HTTPS/TLS for data in transit.
  • Implement Always Encrypted or Transparent Data Encryption (TDE) for at-rest protection.
  • Apply proper access controls to APIs and data stores.

4.1.5 D – Denial of Service (Threat to Availability)

4.1.5.1 What It Is

A Denial-of-Service (DoS) attack aims to prevent legitimate users from accessing your system. This can occur by exhausting resources like CPU, memory, database connections, or network bandwidth.

4.1.5.2 Examples
  1. API Request Flooding – Overwhelming the API gateway with excessive requests.
  2. Resource Exhaustion – Uploading massive files to fill storage.
  3. Single Point of Failure – One failing service blocking the entire system.
.NET Mitigation Example: Rate Limiting in ASP.NET Core 8
builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("api", limiterOptions =>
    {
        limiterOptions.Window = TimeSpan.FromSeconds(10);
        limiterOptions.PermitLimit = 50;
        limiterOptions.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        limiterOptions.QueueLimit = 10;
    });
});

app.UseRateLimiter();

Key Takeaways:

  • Rate limit public endpoints.
  • Use the circuit breaker pattern with libraries like Polly.
  • Scale horizontally and enable autoscaling in Azure or Kubernetes.

4.1.6 E – Elevation of Privilege (Threat to Authorization)

4.1.6.1 What It Is

Elevation of privilege occurs when a user or service gains access to capabilities they should not have.

4.1.6.2 Examples
  1. User Accessing Admin Functions – An ordinary account exploiting an API flaw to access administrative endpoints.
  2. Service Misconfiguration – A background job with excessive privileges can modify unrelated data stores.
.NET Mitigation Example: Policy-Based Authorization
services.AddAuthorization(options =>
{
    options.AddPolicy("AdminOnly", policy =>
        policy.RequireRole("Administrator"));
});

// Controller
[Authorize(Policy = "AdminOnly")]
public IActionResult GetAllOrders() => ...

Best Practices:

  • Use least privilege for accounts and services.
  • Segregate duties between services.
  • Validate authorization at every trust boundary.

4.2 Mapping STRIDE Threats to DFD Elements

4.2.1 A Quick-Reference Table for Practical Application

STRIDE CategoryExternal EntitiesProcessesData StoresData Flows
SpoofingFake user accounts, forged credentialsService impersonationN/AFake source IP or token in API call
TamperingN/ACode injectionUnauthorized DB record changeAltered HTTP payload
RepudiationDenied user actionsNo admin action logsMissing audit recordsAbsent transaction IDs in logs
Information DisclosurePhishing-based data leaksVerbose error messagesUnencrypted sensitive fieldsSniffed data packets
Denial of ServiceBotnet floodsCPU/memory exhaustionDB connection overloadQueue saturation
Elevation of PrivilegeCompromised low-level accountRole bypass exploitUnauthorized schema changesAPI call escalation through forged JWT

5 Case Study Setup: A Real-World .NET Microservices Application

To bridge the gap between theory and practice, we’ll use a real-world reference application that is widely known in the .NET ecosystem—eShopOnContainers. This application is an open-source microservices-based e-commerce platform maintained by Microsoft, designed to demonstrate cloud-native principles, containerization, and modern .NET development practices. Because it’s production-like in scope, it’s an excellent model for threat modeling and STRIDE analysis. In our case study, we’ll walk through its architecture, identify the critical trust boundaries, and set the stage for detailed threat modeling. We will focus on the security implications of each component, understanding not just what each service does, but how it can be targeted and defended.

5.1 Introducing the “eShopOnContainers” Scenario

The eShopOnContainers architecture represents a fairly standard enterprise-grade microservices deployment pattern. It incorporates multiple independently deployed services, API gateways, asynchronous messaging, and multiple persistence layers. By design, it is complex enough to capture a wide range of security challenges faced in production systems. For our purposes, we’ll assume this deployment is hosted in Azure Kubernetes Service (AKS) with public-facing APIs protected by HTTPS, internal services communicating over private networking, and CI/CD pipelines deploying to each service.

5.1.1 Using a Realistic E-commerce Reference Application as our Model

In this scenario, our fictional online retail company runs eShopOnContainers as its core e-commerce platform. The platform enables customers to browse products, manage shopping baskets, place orders, and track shipments. Internally, the system also integrates with payment services, inventory management, and admin dashboards. From a security standpoint, we must account for:

  • Public-facing endpoints that must be secured against unauthorized access and malicious traffic
  • Sensitive customer data that must be encrypted both in transit and at rest
  • Inter-service communication that must be authenticated and authorized even inside the cluster
  • The need for secure handling of asynchronous messages and events
  • Protection against common threats like injection attacks, spoofing, data leakage, and denial of service

5.2 High-Level Architecture Overview

The eShopOnContainers architecture can be broken into several key building blocks. Each plays a unique role in delivering functionality to the customer while maintaining the operational integrity of the platform. For threat modeling, these building blocks also represent distinct trust zones and attack surfaces.

5.2.1 API Gateway (YARP)

The API Gateway serves as the single entry point for all client requests. In eShopOnContainers, YARP (Yet Another Reverse Proxy) is used to route requests from the public internet to the appropriate microservices inside the Kubernetes cluster. From a security perspective, the API Gateway is a critical choke point—it’s the first line of defense for authentication, rate limiting, and request validation. Without proper hardening, attackers could flood backend services with traffic, send malformed payloads, or attempt to bypass authentication entirely. Example of securing YARP in ASP.NET Core:

builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))
    .AddTransforms(builderContext =>
    {
        builderContext.AddRequestTransform(async transformContext =>
        {
            // Enforce HTTPS
            if (!transformContext.HttpContext.Request.IsHttps)
            {
                throw new InvalidOperationException("HTTPS is required.");
            }
        });
    });

Key security responsibilities at the API Gateway include validating JWT tokens before routing, applying global rate limits, rejecting requests with suspicious patterns (such as unexpected query parameters), and sanitizing headers to prevent injection attacks.

5.2.2 Identity Service (Duende IdentityServer)

The Identity Service provides authentication and authorization for the platform. Using Duende IdentityServer, it issues access tokens, refresh tokens, and ID tokens for clients and services. This service enforces identity verification for customers, administrators, and service-to-service calls. Without strong token signing and validation, spoofing and elevation of privilege attacks become trivial. Security considerations for the Identity Service include:

  • Storing signing keys securely in Azure Key Vault
  • Enforcing short token lifetimes to reduce replay attack windows
  • Implementing OAuth 2.1 and OpenID Connect best practices
  • Using Proof Key for Code Exchange (PKCE) for public clients Example of configuring IdentityServer with secure token signing:
builder.Services.AddIdentityServer()
    .AddSigningCredential(new X509Certificate2("mycert.pfx", "password"))
    .AddInMemoryClients(Clients.Get())
    .AddInMemoryIdentityResources(Resources.GetIdentityResources())
    .AddInMemoryApiScopes(Resources.GetApiScopes())
    .AddTestUsers(TestUsers.Users);

5.2.3 Business Microservices (Catalog, Ordering, Basket)

The Catalog Service manages product listings, the Ordering Service handles order creation and processing, and the Basket Service maintains customer shopping carts. These microservices are exposed via the API Gateway to clients but may also communicate directly with each other. Security considerations for business services include:

  • Validating all input to prevent SQL injection or JSON injection
  • Implementing role-based and policy-based authorization to protect sensitive operations
  • Protecting against information disclosure by avoiding verbose error messages in production For example, using ASP.NET Core’s policy-based authorization in the Ordering Service:
services.AddAuthorization(options =>
{
    options.AddPolicy("OrderOwner", policy =>
        policy.RequireAssertion(context =>
        {
            var orderId = context.Resource as int?;
            var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
            return OrderBelongsToUser(orderId, userId);
        }));
});

5.2.4 Asynchronous Communication (RabbitMQ / Azure Service Bus)

The platform uses RabbitMQ or Azure Service Bus to send and receive integration events between services. This decouples services and improves resilience, but it also introduces new security requirements. Without authentication and message signing, attackers could publish malicious events or modify messages in transit. Example of signing messages before publishing to Azure Service Bus:

var messageBody = JsonSerializer.Serialize(orderEvent);
var signature = SignMessage(messageBody, secretKey);

var message = new ServiceBusMessage(messageBody);
message.ApplicationProperties["Signature"] = signature;
await sender.SendMessageAsync(message);

On the receiving side, signatures are verified before processing the event. This protects against tampering and spoofing inside the message bus.

5.2.5 Data Persistence (SQL Server, Redis)

The platform uses SQL Server for relational data (orders, products, user accounts) and Redis for caching basket data. Data stores are frequent targets for information disclosure and tampering attacks. Mitigation strategies include:

  • Enabling Transparent Data Encryption (TDE) or Always Encrypted in SQL Server
  • Restricting database access to specific service identities using Azure Managed Identity
  • Enforcing TLS for Redis connections to prevent sniffing Example of connecting to Redis with TLS in .NET:
var redis = ConnectionMultiplexer.Connect("myredis.cache.windows.net:6380,password=... ,ssl=True,abortConnect=False");

5.2.6 Client Application (Web SPA)

The Single Page Application (SPA) serves as the primary customer interface. Built with Angular, React, or Blazor WebAssembly, it communicates with the API Gateway via HTTPS. Client security considerations include:

  • Never storing sensitive data (like access tokens) in insecure browser storage
  • Implementing Content Security Policy (CSP) headers to reduce XSS risk
  • Using secure cookie settings (HttpOnly, Secure, SameSite) for session data Example of configuring cookies in ASP.NET Core:
services.ConfigureApplicationCookie(options =>
{
    options.Cookie.HttpOnly = true;
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    options.Cookie.SameSite = SameSiteMode.Strict;
});

While the SPA is considered an untrusted client (since it runs in the user’s browser), its design must still limit the damage if an attacker attempts to manipulate requests or inject malicious scripts.


6 Practical Threat Modeling, Part 1: Diagramming the Microservices System

Applying threat modeling to a microservices architecture requires a visual representation that shows the movement of data, the points where trust levels change, and the relationships between components. The most effective tool for this is the Data Flow Diagram (DFD). For the eShopOnContainers case study, the DFD serves as the foundation for our STRIDE analysis because it explicitly shows the processes, data stores, and external entities involved in system operations. The goal is to create a diagram that is detailed enough for security analysis yet abstract enough to avoid overwhelming complexity.

6.1 Creating the “eShopOnContainers” DFD

Building the DFD begins with identifying every process, data store, and external entity that participates in the application’s operation. The DFD for eShopOnContainers will include both public-facing and internal components, along with all communication paths between them. This ensures we have a complete view of the architecture before applying security analysis.

6.1.1 Identifying All Processes, Data Stores, and External Entities

Processes represent the running services or applications that handle data:

  • API Gateway (YARP) – Routes client requests to appropriate microservices.
  • Identity Service (Duende IdentityServer) – Handles authentication and token issuance.
  • Catalog Service – Manages product data and exposes endpoints for browsing products.
  • Basket Service – Maintains customer shopping cart state using Redis as a backing store.
  • Ordering Service – Manages order creation, payment processing initiation, and order tracking.
  • Event Bus Handler – Publishes and subscribes to integration events over RabbitMQ or Azure Service Bus. Data Stores persist information for future retrieval:
  • SQL Server – Holds relational data including users, orders, catalog items.
  • Redis Cache – Stores basket information for fast retrieval.
  • Blob Storage (optional) – Used for storing product images or receipts. External Entities represent users or systems outside our control:
  • Customer Browser SPA – A Single Page Application running in the user’s browser.
  • Admin Dashboard – Used by internal staff for managing catalog and orders.
  • Payment Provider – External payment gateway for processing transactions.
  • Shipping Partner API – External logistics provider for tracking deliveries. In a DFD, each of these elements will be represented with the standard notation: rectangles for external entities, circles/ovals for processes, and open-ended rectangles for data stores.

6.1.2 Mapping All Key Data Flows Between Services

The next step is to connect each entity, process, and data store with labeled arrows showing the direction and nature of the data flow. For eShopOnContainers, key flows include:

  • Customer Browser SPA → API Gateway: HTTPS requests for browsing products, adding to basket, placing orders.
  • API Gateway → Identity Service: Token validation and authentication requests.
  • API Gateway → Catalog Service: Product listing and search queries.
  • API Gateway → Basket Service: Basket retrieval and updates.
  • Basket Service ↔ Redis: Basket read/write operations.
  • API Gateway → Ordering Service: Initiating order placement.
  • Ordering Service → SQL Server: Writing order records and updating status.
  • Ordering Service → Payment Provider: Secure API calls to initiate payment processing.
  • Ordering Service → Event Bus Handler: Publishing “OrderCreated” events.
  • Catalog Service ↔ SQL Server: Reading/writing product data.
  • Admin Dashboard → API Gateway: Administrative commands for product management.
  • Event Bus Handler → Shipping Partner API: Sending shipping requests and receiving tracking updates. Each flow should be evaluated for the type of data transmitted (PII, financial data, public content) because this affects which STRIDE categories will apply most strongly.

6.1.3 Drawing and Labeling the Critical Trust Boundaries (e.g., Internet → Gateway, Gateway → Internal Services, Service → Database)

Trust boundaries are where the security posture changes—where you cannot assume that data or requests can be trusted without verification. In the eShopOnContainers DFD, we can identify several critical boundaries:

  • Boundary 1: Public Internet → API Gateway This is the most hostile boundary. All incoming traffic originates from an untrusted network. The API Gateway must authenticate requests, enforce TLS, apply rate limits, and validate inputs before forwarding to internal services.
  • Boundary 2: API Gateway → Internal Microservices Even though the traffic is now inside the cluster, it’s still important to authenticate inter-service calls, ideally using mutual TLS (mTLS) and signed JWT tokens issued by the Identity Service. This prevents an attacker who has compromised one service from freely accessing others.
  • Boundary 3: Microservices → Data Stores Services must authenticate to databases using unique credentials or managed identities, not shared accounts. Queries must be parameterized to prevent injection attacks, and all sensitive data must be encrypted at rest.
  • Boundary 4: Internal Services → External APIs (Payment Provider, Shipping Partner) Outbound calls to third-party APIs must be protected with API keys or OAuth tokens. Responses should be validated and sanitized before use, as external APIs can also be compromised. Visualizing these boundaries in the DFD clarifies where security controls must be applied. For example, the API Gateway is a critical choke point for incoming traffic, while the Event Bus requires authentication and message signing to prevent spoofing and tampering between services. If this were drawn, the diagram would depict the API Gateway in the DMZ zone, microservices in an internal trusted zone, and data stores in a protected persistence layer, with external services on the opposite side of controlled egress points. For example, in code, enforcing mTLS between the API Gateway and a microservice in ASP.NET Core 8 could look like this:
builder.WebHost.ConfigureKestrel(options =>
{
    options.ConfigureHttpsDefaults(httpsOptions =>
    {
        httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
        httpsOptions.SslProtocols = SslProtocols.Tls13;
    });
});

This ensures that only clients with valid certificates can communicate with the service, effectively securing the trust boundary. By completing this DFD with all entities, processes, flows, and trust boundaries clearly marked, we create the foundation for the next stage—systematically applying STRIDE to identify and prioritize threats for each component and interaction.


7 Practical Threat Modeling, Part 2: Applying STRIDE Threat by Threat

With the eShopOnContainers Data Flow Diagram (DFD) defined, we now move into applying the STRIDE methodology component by component. The aim is to identify threats for each element and map them to realistic mitigations that a .NET architect could implement. This section is a systematic security walkthrough of the architecture, examining each major system element through the STRIDE lens. Where possible, we will include code-level patterns that can be integrated directly into production systems.

7.1 A Systematic Walkthrough of the DFD

Each component will be evaluated for relevant STRIDE categories based on its function, exposure, and trust boundaries. We will focus on attack feasibility, potential impact, and practical defenses that fit into a modern .NET-based microservices architecture.

7.1.1 Analyzing the API Gateway

The API Gateway (YARP in this architecture) is the primary entry point for both customer-facing and admin-facing operations. It exists at the most hostile trust boundary: the public internet. As such, it is a natural target for spoofing and denial-of-service attacks.

7.1.1.1 Spoofing Threats and Mitigations (JWT Validation)

Spoofing here typically involves clients attempting to present forged or stolen access tokens to impersonate other users or services. Without robust token validation, the API Gateway could unwittingly forward requests from malicious actors to internal services. Mitigation Strategy:

  • Enforce strict JWT signature validation using a trusted identity provider.
  • Validate the token issuer, audience, and expiry before processing the request.
  • Implement short-lived access tokens and rotate signing keys regularly.
  • Use HTTPS exclusively to prevent token interception in transit. Code Example – JWT Validation in API Gateway:
builder.Services.AddAuthentication("Bearer")
    .AddJwtBearer("Bearer", options =>
    {
        options.Authority = "https://identity.eshop.com";
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = "https://identity.eshop.com",
            ValidateAudience = true,
            ValidAudience = "eshop_api",
            ValidateLifetime = true,
            ClockSkew = TimeSpan.FromSeconds(30),
            RequireSignedTokens = true
        };
    });
app.UseAuthentication();
app.UseAuthorization();
7.1.1.2 Denial of Service Threats and Mitigations (Rate Limiting, Circuit Breakers)

A DoS attack can be initiated by overwhelming the gateway with requests, consuming compute, memory, and backend service capacity. Even if requests are validly authenticated, excessive volume can still degrade service. Mitigation Strategy:

  • Apply fixed-window or sliding-window rate limiting policies at the gateway level.
  • Implement IP-based throttling for unauthenticated requests.
  • Use the circuit breaker pattern to stop routing requests to unhealthy backend services.
  • Integrate CDN-level protection (Azure Front Door, Cloudflare) for volumetric attacks. Code Example – ASP.NET Core 8 Rate Limiting:
builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("gatewayPolicy", limiterOptions =>
    {
        limiterOptions.Window = TimeSpan.FromSeconds(10);
        limiterOptions.PermitLimit = 100;
        limiterOptions.QueueLimit = 20;
    });
});
app.UseRateLimiter();

7.1.2 Analyzing the Identity Service

The Identity Service handles token issuance, user authentication, and claims management. Any compromise here has system-wide implications, making it a high-value target for attackers.

7.1.2.1 Information Disclosure Threats (Credential Leakage) and Mitigations (Secure Hashing, TLS)

Information disclosure in an identity context often means credential theft, token leakage, or exposure of sensitive claims. This could happen through improperly configured HTTPS, verbose error responses, or storing passwords in plaintext. Mitigation Strategy:

  • Use strong password hashing algorithms (PBKDF2, bcrypt, or Argon2) with unique salts per password.
  • Enforce HTTPS/TLS 1.3 for all identity endpoints.
  • Ensure that no sensitive claims (like password hashes) are ever returned to clients. Code Example – Secure Password Hashing in ASP.NET Core Identity:
var hasher = new PasswordHasher<ApplicationUser>();
var hashedPassword = hasher.HashPassword(user, plainTextPassword);
// Verify later
var verificationResult = hasher.VerifyHashedPassword(user, hashedPassword, attemptPassword);
7.1.2.2 Tampering Threats (Claim Modification) and Mitigations (Signed Tokens)

Tampering here refers to attackers modifying claims inside tokens or altering token payloads in transit. If the Identity Service does not sign tokens securely or services fail to validate signatures, an attacker can gain unauthorized access. Mitigation Strategy:

  • Sign tokens using asymmetric keys (RSA/ECDSA) stored in a secure key vault.
  • Validate token signatures on every request.
  • Rotate signing keys periodically. Code Example – Duende IdentityServer with RSA Key:
builder.Services.AddIdentityServer()
    .AddSigningCredential(new RsaSecurityKey(RSA.Create(2048)))
    .AddInMemoryClients(Clients.Get())
    .AddInMemoryApiScopes(ApiScopes.Get())
    .AddTestUsers(TestUsers.Users);

7.1.3 Analyzing the Ordering Microservice

The Ordering Service coordinates the most sensitive business processes: payment initiation, order creation, and tracking. It processes both customer and admin actions.

7.1.3.1 Repudiation Threats (Denying an Order) and Mitigations (Audit Logging)

Repudiation occurs when a user claims they never placed an order and the system lacks definitive evidence. Without secure, immutable logs, disputes become hard to resolve. Mitigation Strategy:

  • Implement append-only, timestamped audit logs with request identifiers.
  • Store logs in a separate, access-controlled store (e.g., Azure Blob with immutability policy).
  • Include correlation IDs in logs to trace transactions end-to-end. Code Example – Serilog Audit Logging:
Log.Information("Order {OrderId} created by user {UserId} at {TimeStamp}",
    order.Id, user.Id, DateTime.UtcNow);
7.1.3.2 Elevation of Privilege Threats (Viewing Others’ Orders) and Mitigations (User ID Claim Checks)

A common elevation scenario is when a customer modifies an API request to retrieve another customer’s order details. Without ownership checks, this leads to unauthorized data access. Mitigation Strategy:

  • Validate resource ownership at the service layer, not just at the gateway.
  • Never rely solely on client-provided identifiers. Code Example – Enforcing Ownership Check in Controller:
[HttpGet("{orderId}")]
public async Task<IActionResult> GetOrder(int orderId)
{
    var order = await _orderService.GetOrderByIdAsync(orderId);
    var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
    if (order.UserId != userId) return Forbid();
    return Ok(order);
}

7.1.4 Analyzing the Event Bus (RabbitMQ/Azure Service Bus)

The event bus enables decoupled communication between services. However, without authentication, encryption, and validation, it becomes a channel for injecting malicious events or leaking sensitive information.

7.1.4.1 Spoofing Threats (Unauthorized Event Publishing) and Mitigations (Bus Authentication)

Spoofing here means a rogue service or attacker publishes fake events onto the bus, tricking other services into acting on them. Mitigation Strategy:

  • Require credentials or service identities for publishing.
  • Enforce topic-level permissions in RabbitMQ or Azure Service Bus. Azure Service Bus Authentication Example:
var client = new ServiceBusClient("<connection-string>");
var sender = client.CreateSender("order-events");
await sender.SendMessageAsync(new ServiceBusMessage("OrderCreated"));
7.1.4.2 Tampering Threats (Modifying Events) and Mitigations (Message Signing)

Tampering here involves altering the payload of events between publisher and subscriber. This could modify orders, inject false shipping notifications, or cause incorrect inventory changes. Mitigation Strategy:

  • Sign event messages with HMAC or asymmetric signatures.
  • Verify signature at the subscriber before processing. Code Example – Signing Event Message:
public static string SignMessage(string message, string key)
{
    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key));
    return Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(message)));
}
7.1.4.3 Information Disclosure Threats (Unauthorized Listeners) and Mitigations (Topic/Queue Permissions)

Unauthorized listeners can subscribe to queues/topics and receive sensitive data, such as customer PII in order events. Mitigation Strategy:

  • Configure topic and queue-level access control policies.
  • Encrypt sensitive data fields inside event payloads. Example – Securing RabbitMQ Consumers:
rabbitmqctl set_permissions -p / myservice ".*" ".*" ".*"

This ensures only authorized services can bind to specific queues.


8 Threat Analysis, Prioritization, and Management

Identifying threats through STRIDE is only part of the process. For a .NET architect, the real value comes from prioritizing those threats, deciding where to allocate resources, and tracking their resolution over time. This is where structured threat documentation, combined with a risk rating system, transforms raw findings into an actionable security plan.

8.1 Documenting Your Findings

The first step after completing STRIDE analysis is to record each identified threat in a standardized format. Without proper documentation, threat modeling results quickly become scattered notes that are hard to track or act on. By using a threat log or register, you ensure each issue is clearly described, assigned, and monitored until resolved.

8.1.1 Creating a Threat Log or Register

A Threat Log is essentially a living artifact—a centralized record of every identified threat, its associated risk, and its current status. In a mature DevSecOps environment, this log should be accessible to architects, developers, testers, and security engineers. Tools like Azure DevOps Boards, Jira, or dedicated platforms such as IriusRisk can host this log, but even a well-structured spreadsheet can work for smaller teams. The log should be updated after each threat modeling session, penetration test, or major architectural change.

8.1.2 Essential Fields: Threat ID, Description, STRIDE Category, Target Element, Mitigation Status

For each threat entry, the following fields should be recorded:

  • Threat ID – A unique identifier, often with a category prefix (e.g., SPF-001 for a spoofing threat).
  • Description – A concise explanation of the threat, including conditions under which it occurs.
  • STRIDE Category – The relevant STRIDE classification (Spoofing, Tampering, etc.).
  • Target Element – The system component affected (API Gateway, Identity Service, etc.).
  • Mitigation Strategy – The planned or implemented countermeasure.
  • Mitigation Status – Current state (Proposed, In Progress, Implemented, Verified).
  • Risk Rating – Calculated using a consistent scoring system (DREAD in our case).

Example entry in a log for eShopOnContainers:

Threat IDDescriptionSTRIDE CategoryTarget ElementMitigation StrategyMitigation StatusRisk Rating
SPF-001Forged JWT tokens accepted by API GatewaySpoofingAPI GatewayValidate JWT issuer, audience, and signatureImplementedHigh

8.2 Prioritizing Threats with a Risk Rating System

Once the threats are documented, they must be prioritized. Not all vulnerabilities are equal—a low-severity disclosure risk in an internal tool may not warrant the same urgency as a high-severity privilege escalation in a public API. Using a risk rating framework helps ensure consistent prioritization across the team.

8.2.1 Introduction to DREAD

The DREAD model is a simple but effective method to score and rank threats. It evaluates five factors: Damage Potential, Reproducibility, Exploitability, Affected Users, and Discoverability. Each factor is typically scored from 0 (lowest) to 10 (highest). The average score determines the overall risk level.

8.2.1.1 Damage Potential

How much harm could the attacker cause if this threat were exploited?

  • High (8–10): Total system compromise, full data breach, irreversible damage.
  • Medium (4–7): Significant service disruption, partial data breach.
  • Low (0–3): Minor inconvenience, no sensitive data compromised.
8.2.1.2 Reproducibility

How consistently can the attack be carried out?

  • High: Works every time without special conditions.
  • Medium: Works intermittently or requires specific timing.
  • Low: Rarely works or requires unusual conditions.
8.2.1.3 Exploitability

How easy is it for an attacker to exploit the vulnerability?

  • High: Requires basic skills, no special tools.
  • Medium: Requires intermediate skills or specific tools.
  • Low: Requires advanced skills, insider access, or specialized hardware.
8.2.1.4 Affected Users

How many users would be impacted?

  • High: All users in the system.
  • Medium: A significant subset of users.
  • Low: Very few or only specific accounts.
8.2.1.5 Discoverability

How easy is it for an attacker to find the vulnerability?

  • High: Publicly documented or obvious from API responses.
  • Medium: Requires some exploration or insider knowledge.
  • Low: Hidden deep within internal logic.

8.2.2 Applying DREAD to Score and Rank Threats from the “eShop” Example

Let’s score three example threats from the eShopOnContainers case study:

Threat 1 – Forged JWT tokens accepted by API Gateway (Spoofing)

  • Damage: 10 (Full impersonation possible)
  • Reproducibility: 9 (If signing key compromised)
  • Exploitability: 8 (Basic token creation tools)
  • Affected Users: 10 (Entire user base at risk)
  • Discoverability: 7 (Easy for skilled attacker to test) Average Score = (10 + 9 + 8 + 10 + 7) / 5 = 8.8 (Critical)

Threat 2 – Missing ownership check in Ordering Service (Elevation of Privilege)

  • Damage: 7 (Access to other customers’ orders)
  • Reproducibility: 9 (Simple parameter modification)
  • Exploitability: 9 (Basic API client)
  • Affected Users: 5 (Only those whose order IDs are guessed)
  • Discoverability: 6 (Easily found during testing) Average Score = (7 + 9 + 9 + 5 + 6) / 5 = 7.2 (High)

Threat 3 – Unencrypted Redis traffic (Information Disclosure)

  • Damage: 6 (Possible leak of basket data)
  • Reproducibility: 8 (Interceptable in network)
  • Exploitability: 6 (Requires network access)
  • Affected Users: 5 (Users with active baskets)
  • Discoverability: 4 (Requires network sniffing capability) Average Score = (6 + 8 + 6 + 5 + 4) / 5 = 5.8 (Medium)

8.2.3 Creating a Prioritized Threat Log Table

Based on the scores, we can create a prioritized remediation plan.

PriorityThreat IDDescriptionSTRIDE CategoryTarget ElementRisk ScoreMitigation Status
1SPF-001Forged JWT tokens accepted by API GatewaySpoofingAPI Gateway8.8 (Critical)Implemented
2EOP-002Missing ownership check in Ordering ServiceElevation of PrivilegeOrdering Service7.2 (High)In Progress
3INF-003Unencrypted Redis trafficInformation DisclosureRedis Cache5.8 (Medium)Planned

This table can be maintained in a collaborative tool so progress is tracked in real-time. Items with the highest scores should be addressed first, and residual risk should be documented for any threats that cannot be fully mitigated.


9 .NET Mitigation Patterns and Security Best Practices

The process of identifying and prioritizing threats through STRIDE and DREAD is incomplete without concrete, actionable countermeasures. For a .NET architect, the next step is implementing patterns and practices that directly map to the risks identified. This section offers a detailed toolkit, covering the full spectrum of STRIDE categories with practical .NET examples that can be integrated into production systems.

9.1 A Practical Toolkit for .NET Architects

These mitigation strategies are grouped by the STRIDE category they address. Each includes design considerations, implementation details, and working code samples suitable for modern .NET 8 applications running in distributed environments.

9.1.1 Authentication (Spoofing): In-depth look at ASP.NET Core Identity, JWTs, OAuth 2.1, OpenID Connect, and mTLS

Spoofing threats target the trust model of your application, attempting to impersonate legitimate users or services. In microservices, authentication is enforced both at the public entry points and between internal services.

ASP.NET Core Identity ASP.NET Core Identity is the foundation for user authentication in many .NET applications. It manages password hashing, lockouts, and account confirmation flows.

services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
    options.Password.RequireDigit = true;
    options.Password.RequiredLength = 12;
    options.SignIn.RequireConfirmedEmail = true;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();

JWT Validation JWTs are the de facto standard for authenticating API requests in distributed .NET applications. Always validate the signature, issuer, audience, and expiration.

builder.Services.AddAuthentication("Bearer")
    .AddJwtBearer("Bearer", options =>
    {
        options.Authority = "https://identity.eshop.com";
        options.RequireHttpsMetadata = true;
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = "https://identity.eshop.com",
            ValidateAudience = true,
            ValidAudience = "eshop_api",
            ValidateLifetime = true,
            RequireSignedTokens = true,
            ClockSkew = TimeSpan.Zero
        };
    });

OAuth 2.1 and OpenID Connect For user-facing applications, OAuth 2.1 with OpenID Connect ensures secure delegated access and identity claims. Public clients like SPAs should use PKCE to prevent authorization code interception.

Mutual TLS (mTLS) Service-to-service authentication can be strengthened with mTLS, ensuring both client and server verify each other’s identity.

builder.WebHost.ConfigureKestrel(options =>
{
    options.ConfigureHttpsDefaults(httpsOptions =>
    {
        httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
        httpsOptions.SslProtocols = SslProtocols.Tls13;
    });
});

9.1.2 Authorization (Elevation of Privilege): Deep dive into Role-Based Access Control (RBAC) and Policy-Based Authorization in ASP.NET Core

Elevation of privilege threats occur when users gain access to actions or resources beyond their permissions.

Role-Based Access Control (RBAC) RBAC assigns permissions to roles rather than individuals. In ASP.NET Core, roles can be enforced via attributes:

[Authorize(Roles = "Administrator")]
public IActionResult GetAllOrders() => ...

Policy-Based Authorization For more granular checks, policy-based authorization evaluates custom requirements at runtime.

services.AddAuthorization(options =>
{
    options.AddPolicy("OrderOwner", policy =>
        policy.RequireAssertion(context =>
        {
            var orderId = context.Resource as int?;
            var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);
            return OrderBelongsToUser(orderId, userId);
        }));
});

Service-to-Service Authorization Internal APIs should enforce claims-based checks on service identity tokens, preventing lateral movement inside the cluster.

9.1.3 Integrity (Tampering): Implementing digital signatures, anti-forgery tokens, and content security policies

Tampering attacks alter data in transit or at rest. Preserving integrity requires verification at every boundary.

Digital Signatures for Event Messages When using RabbitMQ or Azure Service Bus, sign messages with HMAC to detect tampering:

public static string SignMessage(string payload, string secretKey)
{
    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secretKey));
    return Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(payload)));
}

Anti-Forgery Tokens For web forms, use anti-forgery tokens to prevent CSRF:

services.AddControllersWithViews(options =>
{
    options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});

Content Security Policy (CSP) Add CSP headers to mitigate injection of malicious scripts:

app.Use(async (context, next) =>
{
    context.Response.Headers.Add("Content-Security-Policy", "default-src 'self'; script-src 'self'");
    await next();
});

9.1.4 Confidentiality (Information Disclosure): Using HTTPS, ASP.NET Core Data Protection APIs, Azure Key Vault, and database encryption (TDE, Always Encrypted)

Information disclosure occurs when unauthorized parties access sensitive data.

HTTPS Everywhere Enforce HTTPS redirection:

app.UseHttpsRedirection();

Data Protection APIs ASP.NET Core’s Data Protection APIs handle encryption for tokens and cookies:

services.AddDataProtection()
    .PersistKeysToAzureBlobStorage(new Uri("https://mystorage.blob.core.windows.net/keys/key.xml"))
    .ProtectKeysWithAzureKeyVault(new Uri("https://myvault.vault.azure.net/keys/dataprotection"), new DefaultAzureCredential());

Azure Key Vault Use Azure Key Vault for storing secrets and encryption keys:

var client = new SecretClient(new Uri("https://myvault.vault.azure.net/"), new DefaultAzureCredential());
KeyVaultSecret secret = client.GetSecret("DbPassword");

Database Encryption Enable Transparent Data Encryption (TDE) or Always Encrypted in SQL Server for sensitive fields.

9.1.5 Availability (Denial of Service): Configuring rate-limiting middleware, the circuit breaker pattern with Polly, and cloud autoscaling

Availability attacks attempt to degrade or stop service delivery.

Rate Limiting Protect endpoints with fixed or sliding window rate limits:

builder.Services.AddRateLimiter(options =>
{
    options.AddSlidingWindowLimiter("default", limiterOptions =>
    {
        limiterOptions.Window = TimeSpan.FromSeconds(60);
        limiterOptions.SegmentsPerWindow = 6;
        limiterOptions.PermitLimit = 100;
    });
});

Circuit Breaker with Polly Prevent cascading failures by using circuit breakers for unreliable dependencies:

services.AddHttpClient("CatalogService", client =>
{
    client.BaseAddress = new Uri("https://catalog.eshop.com/");
})
.AddTransientHttpErrorPolicy(p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));

Cloud Autoscaling In Azure Kubernetes Service, configure horizontal pod autoscaling (HPA) to handle traffic spikes without manual intervention.

9.1.6 Non-Repudiation (Repudiation): Best practices for secure and comprehensive logging with frameworks like Serilog

Non-repudiation ensures that actions are provable and cannot be denied after the fact.

Immutable Logging Write logs to append-only storage with retention policies.

Structured Logging with Serilog

Log.Logger = new LoggerConfiguration()
    .Enrich.WithProperty("Application", "OrderingService")
    .WriteTo.File("logs/audit-.log", rollingInterval: RollingInterval.Day)
    .WriteTo.Seq("http://seq-server")
    .CreateLogger();

Correlation IDs Include correlation IDs in logs to trace actions across services:

app.Use(async (context, next) =>
{
    var correlationId = context.TraceIdentifier;
    context.Response.Headers["X-Correlation-ID"] = correlationId;
    await next();
});

10 Automating and Integrating: Threat Modeling in a DevSecOps World

Threat modeling becomes exponentially more valuable when it is not a one-off exercise but a continuous part of the software delivery lifecycle. In modern DevSecOps, the ability to integrate threat identification, mitigation tracking, and verification into ongoing workflows ensures security remains a living component of the architecture rather than a static document gathering dust. This section explores how to operationalize threat modeling for a .NET-based ecosystem, keeping pace with agile delivery and frequent deployments.

10.1 From Static Document to Living Artifact

Traditional threat models are static diagrams and spreadsheets—useful during design but quickly outdated once features change. In Agile and DevOps environments, code evolves daily, and infrastructure may be redeployed multiple times per week. Without automation, threat models suffer from “model drift,” where they no longer accurately reflect the live system.

10.1.1 The Challenge of “Threat Model Drift” in Agile and DevOps

Model drift happens when:

  • New microservices are added but not represented in the DFD.
  • Data flows change (e.g., a service now sends data to a new API) without updates to threat analysis.
  • Security mitigations are implemented but not reflected in documentation.
  • Legacy security controls remain documented even though they have been removed or replaced. In a microservices-heavy .NET system, these changes can occur weekly. Drift creates false confidence—teams assume a threat is mitigated because the document says so, even if the actual deployment no longer matches.

10.1.2 The Goal: “Threat Modeling as Code”

The solution is to treat threat modeling as code. Instead of maintaining diagrams in proprietary formats and static Word documents, define system components, data flows, and threats in version-controlled files. Benefits include:

  • Version history – You can track how the threat model evolves with the codebase.
  • Pull request reviews – Threat model changes can be peer-reviewed alongside application changes.
  • Automation – Changes in infrastructure-as-code (IaC) files can trigger updates to the threat model automatically. Tools like Threagile and IriusRisk allow models to be represented in YAML/JSON, where each microservice, trust boundary, and threat is an object that can be linted and tested. This creates a pipeline where every deployment includes both code changes and updated threat models.

10.2 Integrating Security into the CI/CD Pipeline

Integrating security into the .NET CI/CD process ensures vulnerabilities are detected and addressed before they hit production. The pipeline becomes the enforcement mechanism for security gates and automated checks.

10.2.1 Sprint Planning: Adding “Abuser Stories” alongside User Stories

User stories define desired features from the perspective of legitimate users. “Abuser stories” define the system’s response to malicious behavior. Example user story:

As a customer, I want to save my shipping address so that I can quickly place orders. Example abuser story: As an attacker, I want to send a malicious payload in the shipping address field to attempt SQL injection, so that I can compromise the database. During sprint planning, architects can review abuser stories alongside user stories and ensure acceptance criteria include relevant security controls. For example, the above story would require input validation, parameterized queries, and SQL injection testing in QA.

10.2.2 During Development: Leveraging Security-Focused Static Analysis (SAST) Tools

Static Application Security Testing (SAST) tools scan source code for vulnerabilities without executing it. In the .NET world, you can integrate tools such as:

  • GitHub Advanced Security (CodeQL) – Detects code patterns that lead to vulnerabilities.
  • SonarQube – Includes C# rules for detecting SQL injection, hardcoded secrets, and unsafe deserialization.
  • Roslyn Analyzers – Run as part of the build to catch security rule violations early. Example GitHub Actions workflow with SAST integration:
name: Build and Security Scan
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Setup .NET
      uses: actions/setup-dotnet@v3
      with:
        dotnet-version: '8.0.x'
    - name: Install dependencies
      run: dotnet restore
    - name: Build
      run: dotnet build --configuration Release
    - name: Run CodeQL Analysis
      uses: github/codeql-action/analyze@v2

10.2.3 At Pull Request: Using Branch Policies to Require a Security Review for High-Impact Changes

Some code changes inherently carry higher security risk—e.g., modifying authentication flows, data access layers, or public API endpoints. Configure branch policies so that:

  • Any pull request touching security-critical code requires review by a designated security champion.
  • Automated threat model diffs run when infrastructure files change, flagging new or altered trust boundaries. For Azure DevOps, you can create a required reviewer group for pull requests tagged with a security-impact label. This ensures no high-risk change is merged without expert review.

10.3 A Survey of Modern Threat Modeling Tools

Automating and integrating threat modeling requires choosing the right tools for your workflow. Options range from diagramming tools to full-fledged automation platforms.

10.3.1 Diagramming and Manual Analysis: Microsoft Threat Modeling Tool

Microsoft’s Threat Modeling Tool (TMT) remains a popular choice for manual STRIDE analysis. It provides a drag-and-drop interface for creating DFDs, automatically identifies potential threats based on component type, and generates mitigation suggestions. Pros include:

  • Familiar interface for teams already in the Microsoft ecosystem.
  • Direct STRIDE integration.
  • Export to Excel for logging threats. Limitations:
  • Manual updates needed to stay in sync with code changes.
  • Not ideal for CI/CD automation.

10.3.2 Open Source Automation: Threagile

Threagile is a YAML-based, open-source threat modeling tool designed for automation.

  • You define your architecture (processes, data flows, trust boundaries) in YAML files stored in your repo.
  • The tool generates diagrams, threat reports, and mitigation checklists from the model.
  • It can be run in CI/CD to fail builds when new unmitigated high-severity threats are detected. Example YAML snippet for an API Gateway in Threagile:
components:
  - id: api-gateway
    type: reverse-proxy
    title: API Gateway
    trust-boundary: internet
    inbound:
      - from: client-browser
        protocol: https
        data-asset: customer-data

10.3.3 Commercial Platforms: IriusRisk, SD Elements, ThreatModeler

Commercial platforms offer enterprise-grade features:

  • IriusRisk – API-driven threat modeling with integration into Jira and CI/CD. Can auto-generate threats from architecture diagrams and IaC templates.
  • SD Elements – Maps software requirements to security controls, generating test cases for QA.
  • ThreatModeler – Collaborative modeling with cloud architecture import from AWS, Azure, and GCP. These tools often provide pre-built libraries of threats and mitigations, reducing the need to start from scratch. They also support integration with ticketing systems so that unmitigated threats are automatically logged as backlog items.

11 Conclusion: Fostering a Lasting Security-First Culture

Adopting STRIDE-based threat modeling within a .NET architecture is not a one-off activity—it’s a shift in how systems are conceived, built, and maintained. A security-first culture goes beyond tools and techniques; it’s about embedding secure thinking into every phase of development, from planning to deployment. The strongest architectures come from teams that see security not as a blocker, but as an enabler of trust, reliability, and long-term viability.

11.1 Recap: The Architect’s Journey from Theory to Practice

We began with the imperative of proactive security, moving away from the “bolt it on later” mindset to the shift-left approach. We explored how to understand and decompose a system into its essential components using Data Flow Diagrams, then applied STRIDE to identify threats systematically. We walked through a realistic microservices case study with eShopOnContainers, mapped threats to components, prioritized them using DREAD, and created a plan for mitigation. Finally, we explored .NET-specific patterns for addressing each STRIDE category, and how to integrate and automate threat modeling into a DevSecOps pipeline so it remains accurate over time. The architect’s role is not just to produce diagrams and code patterns, but to maintain a living security blueprint that evolves alongside the product.

11.2 Threat Modeling is a Team Sport: Evangelizing Security

The most successful threat modeling efforts happen when security is distributed across the team, not centralized in a single role. Architects, developers, testers, and product owners all have perspectives that strengthen the model. Security is a shared accountability that thrives when diverse roles collaborate.

11.2.1 Involving Developers, Testers, and Product Owners in the Process

Developers bring insight into code-level realities and can identify where proposed mitigations will integrate smoothly or cause friction. Testers can validate security controls through automated and manual testing, simulating both legitimate and malicious behaviors. Product owners ensure that security aligns with business goals and user expectations, preventing conflicts where security measures could unintentionally harm usability. Collaborative threat modeling workshops—where the DFD is updated on-screen and STRIDE is applied in real-time—can uncover issues early while building security literacy across the team.

11.2.2 Making Security Everyone’s Responsibility

Security can’t be an afterthought owned only by a specialized security team. Every commit, configuration change, and deployment pipeline adjustment has the potential to affect the system’s threat surface. Embedding security responsibilities into job descriptions, performance metrics, and team rituals (like sprint reviews) helps normalize it as part of day-to-day work. Encourage “security champions” within each discipline—developers, QA, DevOps—who advocate for best practices, review security-related pull requests, and mentor peers.

11.3 Your Next Steps and Further Resources

Creating a lasting security-first culture starts with small, deliberate steps that demonstrate value and build momentum.

11.3.1 A Call to Action: Start Small, Model One Feature

If threat modeling is new to your team, don’t attempt to model the entire system at once. Start with one high-impact feature—perhaps a new API endpoint or a data flow handling sensitive customer information. Build its DFD, apply STRIDE, document threats, and design mitigations. Use this as a template to gradually expand modeling coverage across the system. The lessons learned in the first cycle will inform a repeatable process that the team can refine and scale.

To deepen expertise and maintain alignment with industry standards, explore these resources:

  • OWASP Threat Modeling Cheat Sheet – Practical guidance for threat modeling at any stage of development.
  • OWASP Top Ten – A regularly updated list of the most critical web application security risks.
  • Microsoft Security Development Lifecycle (SDL) – Comprehensive guidelines for integrating security throughout the SDLC.
  • Microsoft Threat Modeling Tool Documentation – Official guide for using the tool to create and analyze DFDs.
  • Threagile Documentation – Open-source “threat modeling as code” framework for automation in CI/CD.
  • NIST SP 800-63 Digital Identity Guidelines – Standards for authentication and identity management.
  • Azure Security Documentation – Best practices for securing .NET applications hosted on Azure, covering services like Key Vault, App Gateway, and managed identities.

A strong security posture emerges not from a single workshop or document, but from the habits a team cultivates. With STRIDE as your lens, DFDs as your map, and DevSecOps as your engine, you can ensure that your .NET architectures remain resilient against evolving threats—while building trust with every user interaction.

Advertisement