Fix cards for C# and ASP.NET Core
A01:2025 Broken Access Control
- Cross-site request forgery (CSRF)CWE-352
Call AddAntiforgery() and, after UseAuthentication() and UseAuthorization(), UseAntiforgery().
- Missing object-level authorization (IDOR and BOLA)CWE-862 · CWE-639 · CWE-863
Read the user's ID, tenant and role from the ClaimsPrincipal your authentication scheme builds, never from the request, and scope every invoice query with tenant_id = $tenant AND (owner_id = $user OR $isBillingAdmin).
- Open redirectCWE-601
Check Url.IsLocalUrl(returnUrl) and redirect with LocalRedirect(returnUrl): IsLocalUrl accepts "/" or "~/" paths not followed by "/" or "\" and free of control characters.
- Path traversalCWE-22 · CWE-23 · CWE-36 · CWE-59
Use Path.GetFullPath(name, baseDir) and serve the result only if it starts with the base plus a trailing separator, compared with StringComparison.Ordinal.
- Server-side request forgery (SSRF)CWE-918
Attach the policy to the connection, not the hostname string.
A02:2025 Security Misconfiguration
- CORS misconfiguration on credentialed APIsCWE-942 · CWE-346
Add a named CORS policy with WithOrigins(allowedOrigins) from configuration and AllowCredentials(), call app.UseCors(), and require the policy on the API endpoints.
- XML external entity (XXE) processingCWE-611 · CWE-776
On .NET, parse untrusted XML with XmlReaderSettings.DtdProcessing = Prohibit and XmlResolver = null (both the modern defaults), so any DOCTYPE throws before an external entity is read (CWE-611) or entities expand (CWE-776).
A04:2025 Cryptographic Failures
- Insecure randomness for security tokensCWE-330 · CWE-338
Generate the token with RandomNumberGenerator.GetBytes(32) (256 bits), Base64Url-encoded.
- Weak password hashingCWE-916 · CWE-759 · CWE-327
Use ASP.NET Core Identity's PasswordHasher<TUser> (PBKDF2-HMAC-SHA512, 128-bit salt) with PasswordHasherOptions.IterationCount = 220_000; .NET 10's default of 100,000 is below OWASP's figure.
A05:2025 Injection
- Code injectionCWE-94 · CWE-95
Do not run request input as code. Roslyn's CSharpScript executes the formula with the server's privileges, and a denylist of dangerous APIs cannot make it safe: it misses Environment.GetEnvironmentVariable and is never complete.
- Cross-site scripting (XSS)CWE-79
Let Razor's @ encode the output: @Model.Name and @Model.Bio HTML-encode & < > " ' and more, in text and in a quoted attribute.
- CSV formula injectionCWE-1236
Spreadsheets run a cell that starts with =, +, -, @, tab or carriage return.
- OS command and argument injectionCWE-78 · CWE-88
Start the process with ProcessStartInfo.ArgumentList and UseShellExecute = false - psi.ArgumentList.Add("--"); psi.ArgumentList.Add(name) - so no shell parses the arguments, and the '--' makes a leading-dash operand data, not an option.
- Server-side template injectionCWE-1336 · CWE-94 · CWE-95
Don't run user input as code: CSharpScript.EvaluateAsync compiles the 'template' as C#.
- SQL injectionCWE-89
Keep CommandText constant and bind values: write $name placeholders and call command.Parameters.AddWithValue("$name", name).
- XPath injectionCWE-643 · CWE-91
Parameterize the query: compile the XPath with $username and $password variables and supply them through a custom XsltContext (ResolveVariable returning an IXsltContextVariable), attached with XPathExpression.SetContext.
A06:2025 Insecure Design
- Regular-expression denial of service (ReDoS)CWE-1333
A validation regex with a nested quantifier backtracks catastrophically on the default engine: a 30-character string pins the request thread.
- Unbounded resource consumptionCWE-770 · CWE-400
Bound every resource a request can consume: read the body with a byte cap (413 over it), cap the batch size (400 over it), and add the rate limiter (AddRateLimiter, a fixed window) keyed on the connection address, not a client header.
- Unrestricted file uploadCWE-434 · CWE-646 · CWE-73 · CWE-770
Decide the type from the bytes, and never let the client pick the name.
A07:2025 Authentication Failures
- Hard-coded secretsCWE-798 · CWE-260
Bind the key with AddOptions<T>().BindConfiguration("Forecast"), mark it [Required] and call ValidateDataAnnotations() and ValidateOnStart() so a missing or blank key stops the app at startup.
- Improper TLS certificate validationCWE-295 · CWE-297 · CWE-296
Never return true from a certificate validation callback. Leave validation to SslStream and change only the trust anchor: set SocketsHttpHandler.SslOptions.CertificateChainPolicy to CustomRootTrust with the partner's CA in CustomTrustStore, and RevocationMode NoCheck if the private CA has no CRL.
- JWT validation in APIs (bearer tokens)CWE-347 · CWE-287
Configure JwtBearer's TokenValidationParameters with the issuer's signing key from configuration (or options.Authority), ValidAlgorithms = [RS256], ValidIssuer, ValidAudience and a ClockSkew of seconds, leaving ValidateIssuer, ValidateAudience, ValidateLifetime, RequireExpirationTime and RequireSignedTokens true.
- Missing authentication for a critical functionCWE-306 · CWE-287
Verify a credential on the server in your authentication handler, then require it on every protected endpoint.
- Session fixation and session cookiesCWE-384 · CWE-613 · CWE-614 · CWE-1004 · CWE-1275
Keep the signed-in user in an authentication cookie, not in ISession, which cannot change its ID: SignInAsync issues a new ticket at sign-in.
A08:2025 Software or Data Integrity Failures
- Insecure deserializationCWE-502
Do not let the payload choose the type. With Newtonsoft.Json keep TypeNameHandling at None and bind to a concrete class; a SerializationBinder blocklist is not enough and even quiets the analyzer.
- Mass assignment (over-posting)CWE-915
Bind the body to an input model record that declares only the fields users may edit (here ProfileUpdate(DisplayName, Bio)), never to the entity, and copy those fields onto the entity yourself.
Not covered in C# yet
These weaknesses have fix cards in other stacks but not in this one.
Supported versions
ASP.NET Core ships with .NET: LTS releases get three years of patches and STS releases two years, and you must stay on the latest patch to be supported. ASP.NET Core support policy.
| Version | Latest | Status | Released | End of support |
|---|---|---|---|---|
| 10.0 (LTS) | 10.0.12 | SupportedThe cards use it. | 11 November 2025 | 14 November 2028 |
| 9.0 (STS) | 9.0.20 | MaintenanceSecurity fixes end on 2026-11-10; move to .NET 10. | 12 November 2024 | 10 November 2026 |
| 8.0 (LTS) | 8.0.31 | MaintenanceSecurity fixes end on 2026-11-10; move to .NET 10. | 14 November 2023 | 10 November 2026 |
| Version | Latest | Status | Released | End of support |
|---|---|---|---|---|
| 10.0 | 10.0.401 | SupportedThe cards were built with SDK 10.0.112 and run on runtime 10.0.12. | 11 November 2025 | 14 November 2028 |
| 9.0 | 9.0.318 | MaintenanceSupport ends with .NET 9 on 2026-11-10. | 12 November 2024 | 10 November 2026 |
| 8.0 | 8.0.425 | MaintenanceSupport ends with .NET 8 on 2026-11-10. | 14 November 2023 | 10 November 2026 |
Build with the SDK band of the runtime you deploy; .NET 11 is a go-live release candidate, not a supported release. .NET SDK release schedule
Minimum secure versions
The lowest release in each supported line that fixes every published advisory listed here. Anything older is missing a security fix.
| Package | At least | Why |
|---|---|---|
.NET runtime and Microsoft.AspNetCore.App | 10.0.12, 9.0.20 or 8.0.31 | The 8 September 2026 servicing releases, the latest on 11 October 2026: an ASP.NET Core denial of service under IIS out-of-process hosting (CVE-2026-69304) and three .NET and Visual Studio CVEs (CVE-2026-69439, CVE-2026-69522, CVE-2026-71328). Advisory |
.NET SDK | 10.0.112 or 10.0.401, 9.0.121 or 9.0.318, or 8.0.131 or 8.0.425 | These SDKs carry the patched runtimes and the dotnet watch fixes for CVE-2026-58649 and, on Linux, CVE-2026-69806. Advisory |
Middleware and security order
- Forwarded headers:
UseForwardedHeaderswithKnownProxiesorKnownNetworksbefore other middleware and beforeUseHsts, so the scheme and client address come from your proxy. - Exception handler, HSTS, HTTPS redirection:
UseExceptionHandlerfirst so it catches what follows, thenUseHstsandUseHttpsRedirection; the developer exception page only in Development. - Static files, then routing:
UseStaticFiles(orMapStaticAssets) andUseRouting. - CORS:
UseCorsbeforeUseAuthenticationandUseAuthorization. - Authentication, authorization, antiforgery:
UseAuthentication,UseAuthorization, thenUseAntiforgery, which must come after both. - Endpoints: Map endpoints with
RequireAuthorization()or a fallback policy, and check resource ownership in the handler.
Secure defaults and settings
| Setting | Value | Why |
|---|---|---|
| Razor output | HTML-encoded | Keep Html.Raw out of user data. |
| Antiforgery | Validated for Razor forms, and for minimal API form posts when UseAntiforgery is in the pipeline | Do not add DisableAntiforgery() to cookie-authenticated form endpoints. |
| Cookie authentication cookie | HttpOnly, SameSite=Lax, SecurePolicy same as request | Set CookieSecurePolicy.Always. |
UseHsts max-age | 30 days | Set MaxAge to at least a year once HTTPS works everywhere; HSTS is not sent for localhost. |
JWT bearer ClockSkew | 5 minutes | Reduce it to seconds. |
Escape hatches to search for
Framework APIs that switch a protection off. Each one is sometimes needed; search for them in review and make each use justify itself.
| API | Risk | Instead |
|---|---|---|
Html.Raw() | Unencoded output. | Encoded output, or a sanitizer for HTML you must render. |
[IgnoreAntiforgeryToken] or DisableAntiforgery() | CSRF on cookie-authenticated endpoints. | Keep antiforgery on form endpoints that use cookies. |
[AllowAnonymous] | Overrides the authorization policy, including a fallback policy. | Use it only on deliberately public endpoints. |
Newtonsoft.Json TypeNameHandling other than None | The payload chooses the types to create. | System.Text.Json or known types. |
ValidateIssuer = false, ValidateAudience = false, ValidateLifetime = false | Accepts tokens issued for another API, by another tenant or after expiry. | Configure the authority and audience. |
SetIsOriginAllowed(_ => true) with AllowCredentials() | Any site can read authenticated responses. | WithOrigins(...). |
EF Core FromSqlRaw with an interpolated string | SQL injection. | FromSql with an interpolated string, which EF turns into parameters. |
Checklist, mapped to ASVS 5.0.0
- v5.0.0-3.4.1 HSTS is on with a max-age of at least a year.
- v5.0.0-3.5.1 Cookie-authenticated form posts validate an antiforgery token.
- v5.0.0-8.2.1 A fallback authorization policy requires authenticated users.
- v5.0.0-8.2.2 Handlers use resource-based authorization or owner-scoped queries.
- v5.0.0-9.1.1 JWT bearer authentication validates the signature, issuer, audience and lifetime.
- v5.0.0-1.2.4 EF Core and ADO.NET queries use parameters.
- v5.0.0-4.1.3 Forwarded headers are accepted only from known proxies.
- v5.0.0-16.5.1
UseExceptionHandlerreturns a generic problem response outside Development.