Skip to content
>_ITDITDWeb Security Platform

By framework

ASP.NET Core security — a production hardening reference

An ASP.NET Core production-hardening reference: a priority checklist plus production errors, secrets (User Secrets/Key Vault), NuGet CVEs, authorization, over-posting, deserialization, and SSRF. Defensive, no attack steps.

Published 2026-07-02 Updated 2026-07-02 6 min read

For: anyone running an app or API on ASP.NET Core. No attack steps here — this is a working reference for hardening: a priority-ordered checklist, per-area guidance, and self-verification. For the cross-framework picture, see the security-by-framework hub.

Priority-ordered hardening checklist

Do this table top-down. P0 is a prerequisite, P1 is the most frequent source of incidents, P2 is ongoing operational hygiene.

P0 ── Prerequisite (do first)

No detailed errors in production / externalize secrets / patch NuGet dependency CVEs fast

P1 ── Top source of incidents

Authorization ([Authorize], default-deny, owner) / over-posting defense (DTOs/[Bind])

P2 ── Operational hygiene

Unsafe deserialization / HTTPS, headers, antiforgery / SSRF

Harden from the foundation up: P0 (prerequisite) → P1 (top source of incidents) → P2 (operational hygiene).
PriorityControlSpecifics (ASP.NET Core)
P0Production errorsUseExceptionHandler. Don't show the Developer Exception Page/detail in prod (correct env check)
P0Externalize secretsDon't hardcode in appsettings.json. Dev = User Secrets, prod = env/Key Vault
P0NuGet dependency CVEsMonitor with dotnet list package --vulnerable/osv-scanner; judge by running version, patch fast
P1Explicit authorization[Authorize] + default-deny (fallback policy) + resource-based/owner checks
P1Over-posting defenseBind to a DTO, not the entity directly. Use [Bind] to limit accepted fields
P2DeserializationDon't use BinaryFormatter. Don't restore untrusted data with an unsafe format
P2HTTPS/headers/CSRFUseHttpsRedirection, UseHsts, antiforgery (CSRF), cookie attributes
P2SSRFAllowlist server-side fetches + block internal IPs/metadata

1. Production error exposure (P0)

  • In production, switch to a generic error via UseExceptionHandler and don't show the Developer Exception Page/detail (get the environment check right, e.g. ASPNETCORE_ENVIRONMENT=Production).
  • Don't leak stack traces or internal structure externally.

2. Externalize secrets (P0)

  • Don't hardcode connection strings or keys in appsettings.json. Use User Secrets in development and environment variables or a cloud secret manager (Key Vault) in production.
  • appsettings.json leaks easily via accidental commit or exposure. Don't commit it to the repo or place it in a public directory (→ keep secrets out of public directories). Rotate promptly if leaked.

3. NuGet dependency CVEs (P0)

4. Authorization (P1 — the top source)

Common (dangerous)

  • forgetting [Authorize] on an endpoint
  • authenticated, but no owner check
  • default leans "allow," unauthenticated passes through
  • no role/policy design, ad-hoc checks

Correct

  • [Authorize] + default-deny via a fallback policy
  • role/policy-based + resource-based authorization for ownership
  • verified on every read/update/delete path
  • authorization built explicitly, not left to defaults

See what IDOR is. Authentication and authorization differ; authorize close to the data.

5. Over-posting (P1)

  • Don't bind entities directly. Bind to an input-only DTO and accept only the fields you need.
  • Or use [Bind] to explicitly limit the accepted fields. Don't let a privilege flag be overwritten from external input.

6. Unsafe deserialization and input (P2)

  • Don't use BinaryFormatter (obsolete/insecure). Don't restore untrusted data with an unsafe format (can lead to RCE under the right conditions) (→ what RCE is).
  • Validate input with model validation (data annotations, etc.) for type/range/allowed values before use.

7. HTTPS, headers, antiforgery (P2)

  • Use UseHttpsRedirection + UseHsts to force HTTPS, and mark cookies secure/httponly/samesite.
  • Use antiforgery tokens (CSRF defense) on forms/state-changing requests (→ what CSRF is).
  • Add security headers (check your own site with the security headers checker).

8. SSRF and server-side fetches (P2)

  • Server-side (HttpClient, etc.) fetching of user-supplied URLs should restrict targets to an allowlist and block reaching internal IPs/metadata (→ what SSRF is). Validate uploads and store them off the public surface.

Verify: is your ASP.NET Core actually hardened?

Building it isn't the end — it's done only once you've checked. These are defensive self-checks against your own environment.

1

No Developer Exception Page in production

Trigger an error in production and confirm detail (stack / Developer Exception Page) isn't shown externally.
2

Secrets aren't in config/repo

Confirm connection strings/keys aren't hardcoded in appsettings.json, and that no secrets are in the repo.
3

Authorization holds

In a test environment, request another user's resource and confirm it is denied (read/update/delete).
4

Dependencies and HTTPS/headers

Confirm dotnet list package --vulnerable/osv is clean, and that HTTPS/HSTS are present via the headers checker.

This site's view: even on a solid foundation, settings and authorization are on you

ASP.NET Core has solid mechanisms for authentication, authorization, and data protection, but production settings and applying authorization are things you must get right per environment and per endpoint. This site is a different stack, but the principle is the same: don't leak internals in production, keep secrets outside config, always write authorization on public entry points, and monitor dependencies for CVEs. A solid foundation only pays off with correct settings and explicit authorization.

FAQ

QWhat should I do first to secure ASP.NET Core?
A

The three P0 items: (1) don't show the Developer Exception Page / detailed errors in production (UseExceptionHandler, correct environment check); (2) externalize secrets instead of hardcoding them in appsettings.json (User Secrets in dev, env vars or Key Vault in prod); (3) machine-monitor NuGet dependency CVEs and patch fast, judging by the running version. Next, move to authorization ([Authorize], default-deny) and over-posting defenses.

QWhere should secrets (connection strings, API keys) go?
A

The rule is: not hardcoded in appsettings.json. In development use User Secrets; in production load from environment variables or a cloud secret manager (Key Vault). appsettings.json tends to end up in the repo and leaks easily via a public directory or an accidental commit. If one leaks, rotate the connection string or key promptly.

QHow do I prevent forgetting authorization attributes?
A

Forget [Authorize] on a controller or endpoint and anyone can reach it without authentication. Lean the default toward deny (a fallback policy that rejects unauthenticated requests), make permissions explicit with role/policy-based authorization, and write resource-owner checks (resource-based authorization). Don't stop at 'logged in = allowed' — verify the owner of the target too.

QWhat is over-posting?
A

If model binding accepts every field into an entity, unexpected fields the user sent (like a privilege flag) can get overwritten — that's over-posting. The fix is to bind to an input-only DTO, or use [Bind] to explicitly limit the accepted fields. Don't bind entities directly.

QWhy is unsafe deserialization dangerous?
A

Restoring untrusted data with an unsafe deserializer like BinaryFormatter can, under the right conditions, lead to remote code execution (RCE). BinaryFormatter is considered obsolete/insecure — don't use it. Verify the provenance of externally-sourced data and restrict to a safe format (e.g. properly-configured JSON) where needed.