Threat model endpoint
Skill tunahanaliozturk/secure-dotnet-skills/skills/threat-model-endpoint
Aegis — 12 judgment-style agent skills for secure, production-grade .NET on Azure (security, design, performance, concurrency, observability). Works with Claude Code, Codex, Cursor, Gemini.
npx -y skills add tunahanaliozturk/secure-dotnet-skills --skill threat-model-endpointAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use when threat-modeling an API endpoint, feature, or data flow in an ASP.NET Core app — to enumerate STRIDE threats and concrete mitigations before building or shipping.
SKILL.md
12.2 KB, as published. Nobody here has run it
Threat-Model Endpoint
Directs the agent to walk an ASP.NET Core endpoint or data flow through all six STRIDE categories, enumerate concrete threats for that specific surface, rank them by likelihood × impact, and produce a threat table that names the .NET mitigation and its current status (present or missing).
When to use
- A new or modified API endpoint crosses a trust boundary (anonymous caller, external system, elevated privilege, sensitive data).
- A feature introduces a funds transfer, authentication change, PII write, or privilege escalation path.
- A security design review is required before a sprint ships to staging or production.
- Post-incident: confirming a class of threat is fully covered across a data flow.
Process
- Name the asset, actors, and trust boundaries. State what data the endpoint reads or writes, who can call it (anonymous, authenticated user, service account, admin), and which trust boundaries the request crosses (network edge → API → database, or API → downstream service).
- Walk STRIDE in order. Address every category — Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege — even if you assess a category as low risk. Skipping silently is a process failure.
- For each category, enumerate the concrete threats for this endpoint. A concrete threat names the attacker goal and the attack vector, not just the abstract label. "Attacker replays a captured JWT to impersonate the transferring user" is concrete; "spoofing could occur" is not.
- Rank by likelihood × impact. Use Critical / High / Medium / Low. Likelihood factors: attacker reachability (unauthenticated vs authenticated), input control, and known exploitation patterns. Impact factors: data sensitivity, financial effect, blast radius.
- For each threat, note the mitigation and whether it is present or missing. Name the ASP.NET Core API, configuration key, or policy that provides the mitigation. "Present" means verifiably wired in the current code; "Missing" means the control is absent or incorrectly configured.
- Output a short threat table (STRIDE category | threat | likelihood × impact | mitigation | status). Surface Critical and High findings as explicit action items at the top.
.NET / Azure checks
Spoofing
- Authentication scheme and JWT validation parameters. Confirm
AddAuthentication().AddJwtBearer(...)setsTokenValidationParameterswithValidateIssuer = true,ValidateAudience = true,ValidateLifetime = true,ValidateIssuerSigningKey = true, and a non-nullIssuerSigningKey. A misconfigured parameter silently accepts forged or expired tokens. - Entra ID integration via
Microsoft.Identity.Web. PreferAddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))over hand-rolled JWT options; it validates issuer, audience (api://<clientId>), andtid/oidclaims automatically and handles multi-tenant and B2C variants. - Short token lifetimes and clock skew. Confirm
ClockSkewis not inflated beyond a few minutes (default is 5 min — acceptable). Long-lived access tokens (exphours away) expand the replay window if a token is stolen. - Service-to-service identity. Downstream service calls should use a managed identity +
DefaultAzureCredential; client-secret credentials stored in config are a spoofing risk if the secret leaks.
Tampering
- Model validation on every state-changing request. Confirm
DataAnnotationsor FluentValidation runs before the handler body executes (via[ApiController]automatic 400 response or a MediatR pipeline behavior). Unvalidated numeric fields, enums, or monetary amounts enable business-logic tampering. - Signed or encrypted tokens for values that must not be altered. If the endpoint consumes a value from a cookie, query string, or header that encodes server-side state (e.g., a transfer token or idempotency seed), it must be protected with
IDataProtectionProvider.CreateProtector("purpose").Protect(...)/Unprotect(...)— not base64 or weak HMAC. - Integrity of persisted data and optimistic concurrency. For records that must not be silently overwritten by concurrent writers, confirm a concurrency token (
[Timestamp]/IsRowVersion()in EF Core) is in use. Without it, a race between two valid requests can result in a lost-update attack where the last writer wins regardless of business rules. - Audit trail immutability. Confirm that audit records written by the endpoint cannot be altered by the same caller that triggered them (write to append-only storage, Azure immutable Blob, or Event Hub).
Repudiation
- Structured audit logging with correlation and trace ids. Every state-changing operation must emit a log entry that includes: the authenticated caller identity (
sub,oid, ornameidentifierclaim), the action taken, the affected resource id, the outcome, and the W3Ctraceparent/ ASP.NET CoreHttpContext.TraceIdentifier. UseILoggermessage templates with named placeholders — never string interpolation. - Log sink durability. Confirm logs flow to a durable, tamper-resistant sink (Azure Monitor / Log Analytics workspace, Application Insights, or a SIEM). Local file sinks are insufficient for repudiation-critical operations.
- Do not log what should not be logged, and do not omit what must be. PII and secrets must be excluded; but caller id, resource id, and outcome must always be present so that a denial cannot succeed.
Information Disclosure
- Response DTOs, not EF entities. Confirm the endpoint returns a purpose-built response DTO, not the EF entity directly. Returning an
ApplicationUserorTransferentity exposes all mapped columns — including internal flags, password hashes, or soft-delete markers — to the caller. UseSelect(e => new TransferDto { … })or AutoMapper with an explicit profile. ProblemDetailswithout internal stack traces. Confirm production usesapp.UseExceptionHandler(...)(notapp.UseDeveloperExceptionPage()) and thatProblemDetailsresponses (builder.Services.AddProblemDetails()) do not include the exceptiondetailorstackTracefields. An unhandled exception leaking an EF connection string or file path in the response body is a High finding.- No PII or secrets in logs or trace attributes. Review every
_logger.Log…call near the endpoint. Token values, passwords, card numbers, and national-id fields must not appear in log messages or OpenTelemetry span attributes. Use redaction (LoggerMessage.Definewith structured templates and a redacting enricher, or ASP.NET Core'sIRedactedLogValue). - Enumeration resistance. Resource ids in responses should not be sequential integers if enumeration risk exists; prefer UUIDs or opaque tokens. 404 and 403 responses should not distinguish "not found" from "found but not yours" to a caller who should not know the resource exists.
Denial of Service
- Rate limiting on expensive or anonymous endpoints. Confirm
builder.Services.AddRateLimiter(...)is registered andapp.UseRateLimiter()is in the pipeline. Apply a fixed-window or sliding-window policy to anonymous endpoints; a token-bucket or concurrency limiter to authenticated but expensive operations (e.g., funds transfers, PDF generation, bulk imports). - Request body size limits. ASP.NET Core's default is 30 MB for form requests. For endpoints that do not accept file uploads, lower the limit via
[RequestSizeLimit(65_536)]orbuilder.WebHost.ConfigureKestrel(k => k.Limits.MaxRequestBodySize = …). - Timeouts on downstream calls.
HttpClientinstances registered viaIHttpClientFactorymust haveTimeoutconfigured or aPolly/Microsoft.Extensions.Http.Resiliencepolicy with a timeout. An uncapped outbound call to a slow dependency can exhaust the ASP.NET Core thread pool. - Async I/O throughout the call stack. Confirm the endpoint and all called services use
async/awaitwithCancellationTokenpropagation. A synchronous blocking call (.Result,.Wait()) holds a thread-pool thread, making the service vulnerable to thread-pool exhaustion under load. - Pagination on collection endpoints. Confirm any endpoint that returns a list enforces a maximum page size (
Math.Min(pageSize, 100)or similar). An unbounded_db.Transfers.ToListAsync()can return millions of rows and exhaust memory.
Elevation of Privilege
- Authorization policies beyond bare
[Authorize].[Authorize]alone only confirms the caller is authenticated. For sensitive operations, require an explicit policy:[Authorize(Policy = "TransferApprover")]backed byAddAuthorization(opts => opts.AddPolicy("TransferApprover", p => p.RequireClaim("scp", "Transfers.Write").RequireRole("FinanceUser"))). Never gate a funds transfer or admin action on authentication alone. scpandrolesclaim validation. For Entra ID-issued tokens, confirm the endpoint checks thescp(delegated) orroles(app) claim for the required scope/role — not just that a valid token exists.Microsoft.Identity.Webexposes[RequiredScope("Transfers.Write")]and[RequiredScopeOrAppPermission(...)]attributes for this.- Least-privilege managed identity. If the endpoint calls Azure services (Storage, Key Vault, Service Bus), the managed identity must hold only the roles required for those calls (
Key Vault Secrets User,Storage Blob Data Contributor). An identity withContributorat the subscription scope is an elevation vector if the endpoint is compromised. - Fallback policy =
RequireAuthenticatedUser. RegisterAddAuthorization(opts => opts.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build())so that any endpoint that inadvertently lacks an[Authorize]attribute is still protected. Without this, adding a new controller without decoration makes it anonymously accessible.
Red flags
| Signal | Why it matters |
|---|---|
[Authorize] with no policy name on a funds-transfer or admin endpoint | Authentication only — any valid user can invoke it regardless of role or scope; a compromised low-privilege account can escalate. |
ValidateAudience = false in JwtBearerOptions | Tokens issued for any audience (any app) are accepted, enabling cross-service token replay by an attacker who obtains a token for a different resource. |
| Endpoint returns an EF entity type directly from the handler | All mapped columns — including internal flags, soft-delete markers, and sensitive fields — are serialized to the caller; information disclosure and overposting risk on write paths. |
No AddRateLimiter / UseRateLimiter on an unauthenticated or expensive endpoint | Open to brute-force, credential-stuffing, or resource-exhaustion attacks with no server-side throttle. |
| Request body size not restricted below the Kestrel default (30 MB) on a non-upload endpoint | A single malicious request can allocate tens of megabytes of memory per connection; amplified across concurrent connections this exhausts the process. |
app.UseDeveloperExceptionPage() active in production, or ProblemDetails emitting stack traces | Unhandled exceptions leak call stacks, file paths, connection strings, and internal type names to unauthenticated callers. |
| No audit log entry for a state-changing operation | A caller can deny performing the operation; regulatory compliance (PCI DSS, SOX, GDPR) typically mandates an immutable audit trail for financial or PII-touching writes. |
_logger.LogInformation($"Transfer {amount} for user {email}") (interpolated, not structured) | String interpolation bakes PII into the log message without structured fields; redaction is impossible after the fact, and the log sink sees the raw value. |
Example
See examples/threat-model-endpoint/.
Related skills
- rate-limiting-review — use to review DoS mitigations including limiter algorithm, partitioning, and 429 semantics.
- dotnet-security-review — use to perform a full lens-by-lens security code review after threat modeling.