ASP.NET Core Security Interview Questions for Senior .NET Developers (2026)

ASP.NET Core security is one of the most consistently tested areas in senior .NET developer interviews. Whether you are applying for a tech lead role, a principal engineer position, or a senior backend role at a security-conscious company, interviewers expect you to go beyond "use HTTPS" and demonstrate a production-level understanding of authentication, authorization, threat modeling, and the security primitives built into the framework itself.
This guide covers 25+ ASP.NET Core security interview questions for senior .NET developers in 2026, organized from foundational concepts through advanced scenarios. Each question includes the answer examiners are looking for โ not just the textbook definition, but the context and trade-offs that distinguish senior answers from mid-level ones.
๐ Want implementation-ready .NET source code you can drop straight into your project? Join Coding Droplets on Patreon for exclusive tutorials, premium code samples, and early access to new content. ๐ https://www.patreon.com/CodingDroplets
Basic: Authentication and Authorization Fundamentals
What is the difference between authentication and authorization in ASP.NET Core?
Authentication establishes who the caller is โ it validates identity through credentials, tokens, or certificates. Authorization determines what an authenticated caller is permitted to do. In ASP.NET Core, these are distinct middleware layers: UseAuthentication() must be registered before UseAuthorization() in the pipeline.
A senior answer adds: failing to call them in the correct order is a common misconfiguration that silently allows unauthenticated requests through to authorization handlers. The pipeline is sequential โ if authentication hasn't run, the HttpContext.User identity is unauthenticated, and policy-based authorization will fail or behave unexpectedly depending on how handlers are written.
How does cookie-based authentication differ from JWT bearer authentication in ASP.NET Core?
Cookie authentication stores session state server-side (or encrypts it into a cookie with Data Protection) and is best suited for browser-driven web applications where CSRF protection is a concern. JWT bearer authentication is stateless โ the token carries all claims and is validated on each request without server-side session lookup, making it the standard choice for APIs consumed by mobile apps, SPAs, and microservices.
The distinction interviewers probe here: cookie authentication requires CSRF mitigation (ASP.NET Core's antiforgery system), while JWT is vulnerable to token theft if stored in localStorage. Senior developers know that HttpOnly cookie storage for JWTs can combine the stateless nature of JWTs with the XSS-resistance of cookies.
What is the purpose of AddAuthentication() versus AddAuthorization() in the DI container?
AddAuthentication() registers authentication services and specifies the default authentication scheme. It does not add any scheme-specific handler itself โ you chain scheme registrations onto it (e.g., .AddJwtBearer(), .AddCookie()). AddAuthorization() registers the authorization services and optionally configures a fallback policy, default policy, and named policies.
A common interview trap: adding a scheme handler (e.g., AddJwtBearer()) without calling AddAuthentication() first causes a InvalidOperationException at runtime. The default scheme matters because middleware calls AuthenticateAsync against it when no explicit scheme is specified on an endpoint.
What is a claims principal in ASP.NET Core and how is it populated?
A ClaimsPrincipal is the authenticated identity representation attached to HttpContext.User. It contains one or more ClaimsIdentity objects, each carrying a set of Claim key-value pairs (e.g., sub, email, role).
In JWT bearer authentication, the principal is populated by the JwtBearerHandler during the AuthenticateAsync step โ it parses and validates the token, maps standard JWT claims to ClaimTypes equivalents, and builds the principal. Senior developers note that claim type mapping is configurable and often needs to be disabled (MapInboundClaims = false) to preserve original JWT claim names like sub and roles instead of having them remapped to long Microsoft URI strings.
Intermediate: JWT, OWASP, and Data Protection
How do you implement JWT refresh token rotation in ASP.NET Core?
Refresh token rotation means issuing a new refresh token every time a refresh token is redeemed, and immediately invalidating the old one. The implementation involves persisting refresh tokens server-side (database or distributed cache) with expiry and a one-time-use flag.
The rotation logic: on /token/refresh, validate the incoming refresh token against the store, issue a new access token and a new refresh token, invalidate the old refresh token in the store, and return both. Concurrency handling matters โ use optimistic locking or a transactional update to prevent a replayed refresh token from succeeding in a race condition.
This topic also links to JWT Authentication vs Reference Tokens in ASP.NET Core APIs, where the trade-offs between self-contained JWTs and opaque reference tokens are explored in depth for enterprise scenarios.
What OWASP Top 10 risks are most relevant to ASP.NET Core API development?
Senior developers are expected to map OWASP risks to concrete framework mitigations:
- A01 Broken Access Control โ mitigated through policy-based authorization, resource-based authorization handlers, and avoiding reliance solely on client-supplied data for access decisions.
- A02 Cryptographic Failures โ mitigated through ASP.NET Core Data Protection for at-rest encryption, enforcing HTTPS with HSTS, and using
PasswordHasher<T>or BCrypt rather than MD5/SHA-1 for passwords. - A03 Injection โ in .NET APIs, SQL injection is mitigated through EF Core parameterized queries; command injection through avoiding
Process.Startwith user input; LDAP injection through proper escaping. - A07 Identification and Authentication Failures โ covered by ASP.NET Core Identity's account lockout, two-factor authentication, and secure token generation.
- A09 Security Logging and Monitoring Failures โ mitigated by structured logging with correlation IDs, logging authentication failures at appropriate levels, and not logging sensitive claim data.
The key insight interviewers look for: OWASP risks are addressed at the framework level and the application code level โ using the right ASP.NET Core primitives is necessary but not sufficient.
How does ASP.NET Core Data Protection work and when should you use it?
The Data Protection API provides cryptographic primitives for protecting and unprotecting data (primarily for anti-forgery tokens, authentication cookies, and short-lived payloads). It uses key rings โ collections of keys with a primary active key and archived keys for decryption of older payloads.
By default, keys are stored in-memory, which breaks in multi-instance deployments (load-balanced environments will be unable to unprotect data generated by other instances). In production, key ring storage must be externalized: PersistKeysToAzureBlobStorage, PersistKeysToDbContext (EF Core), or file system with DPAPI/Azure Key Vault encryption.
Senior developers are expected to know: Data Protection is not just for cookies. It's appropriate for any short-lived sensitive payload โ password reset tokens, email confirmation tokens, CSRF tokens. Microsoft Docs coverage: ASP.NET Core Data Protection Overview.
How do you prevent Cross-Site Request Forgery (CSRF) in an ASP.NET Core application?
CSRF attacks trick an authenticated browser into making unintended requests on behalf of the user. ASP.NET Core's antiforgery system generates and validates request tokens using the Data Protection API.
For Razor Pages and MVC forms, @Html.AntiForgeryToken() and [ValidateAntiForgeryToken] handle this automatically. For AJAX/SPA clients, the double-submit cookie pattern is used โ the token is sent in a request header (e.g., X-CSRF-TOKEN) alongside the cookie, and the middleware validates both match.
Critical nuance: APIs that use JWT bearer authentication and require Authorization: Bearer headers are not vulnerable to CSRF because cross-origin requests cannot include custom headers without CORS preflight. Antiforgery is only relevant for cookie-authenticated endpoints.
What is Cross-Origin Resource Sharing (CORS) and how do you configure it securely in ASP.NET Core?
CORS is a browser mechanism that restricts which origins can make cross-origin requests to an API. ASP.NET Core's CORS middleware (UseCors()) intercepts preflight OPTIONS requests and adds Access-Control-Allow-* headers.
Secure configuration means: specifying explicit allowed origins (never AllowAnyOrigin() with AllowCredentials() โ this is a misconfiguration that throws at startup in ASP.NET Core 2.2+), restricting allowed methods and headers, and setting appropriate MaxAge for preflight caching.
Named policies allow different CORS rules for different endpoint groups โ useful when you have a public API and an admin API on the same host. A frequently missed point: CORS is a browser enforcement mechanism. It does not protect against non-browser clients (curl, Postman, server-to-server calls) โ those bypass CORS entirely.
Advanced: Policy-Based Authorization, Secrets, and Production Security
How does resource-based authorization differ from role-based authorization in ASP.NET Core?
Role-based authorization makes access decisions based on the caller's role claims alone (e.g., [Authorize(Roles = "Admin")]). It cannot account for which resource is being accessed.
Resource-based authorization passes the specific resource instance into the authorization handler, allowing fine-grained decisions. For example, "can this user edit this document" depends on the document's owner field. The implementation uses IAuthorizationService.AuthorizeAsync(user, document, requirement) with a custom AuthorizationHandler<TRequirement, TResource>.
The pattern matters for any multi-tenant application where data scoping must be enforced at the authorization layer rather than relying solely on filtering in the data access layer.
What is a policy-based authorization requirement in ASP.NET Core?
An IAuthorizationRequirement is a marker interface representing what needs to be satisfied for access to be granted. It carries no logic itself โ logic lives in AuthorizationHandler<TRequirement>.
A requirement can be satisfied by multiple handlers (OR semantics per handler), and multiple requirements in a policy must all pass (AND semantics per requirement). This allows composable, testable authorization rules.
Example: a MinimumAgeRequirement carries an integer. The handler checks the user's DateOfBirth claim, computes age, and calls context.Succeed(requirement) or context.Fail(). Senior answers include: handlers can be registered as services with full DI support, allowing them to query databases or call external services.
How do you manage application secrets securely across development, staging, and production in ASP.NET Core?
Development: .NET User Secrets (dotnet user-secrets) store configuration outside the project directory, never checked into source control. Loaded automatically in the Development environment.
Production: Environment variables are the minimum acceptable approach. Preferred: Azure Key Vault, AWS Secrets Manager, or HashiCorp Vault integrated via the configuration system. ASP.NET Core's configuration providers are composable โ vault-sourced values override appsettings.json values without code changes.
Anti-pattern: storing production secrets in appsettings.Production.json committed to the repository. Another anti-pattern: loading secrets from environment variables and logging the entire configuration object on startup (a surprisingly common logging mistake that exfiltrates secrets to log aggregators).
This connects to the detailed coverage in .NET Secrets Management: Azure Key Vault vs User Secrets vs Environment Variables.
How does ASP.NET Core handle Content Security Policy (CSP) and security headers?
ASP.NET Core does not include built-in CSP or security header middleware โ these must be added manually or via a library like NWebsec or Helmet-equivalent packages.
The standard approach: middleware that adds Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, and Permissions-Policy headers to every response. These headers instruct browsers to restrict resource loading, prevent clickjacking, and disable MIME-type sniffing.
Senior developers are expected to know: CSP is complex to configure without breaking legitimate functionality. A report-only mode (Content-Security-Policy-Report-Only) allows you to deploy and observe violations before enforcing. CSP directives like script-src 'nonce-{random}' prevent inline script injection without blocking all scripts.
What is the difference between [Authorize] with a fallback policy versus RequireAuthorization() on endpoint groups?
A fallback policy (configured via options.FallbackPolicy) applies to all endpoints that have no explicit authorization attribute. It is the "default secure" approach for applications where most endpoints require authentication โ you configure the fallback once and explicitly opt out with [AllowAnonymous] where needed.
RequireAuthorization() is the Minimal API / endpoint routing equivalent โ it applies authorization to a specific endpoint or group. The two approaches compose: a fallback policy covers any endpoint not explicitly configured, while RequireAuthorization() provides per-endpoint overrides.
The senior insight: fallback policies do not apply to endpoints that have any [Authorize] attribute โ if an endpoint explicitly uses [Authorize], only its specified policies apply, not the fallback.
How do you audit and log security events in an ASP.NET Core application?
Security event logging should capture: failed authentication attempts, authorization failures, privilege escalation events, and changes to sensitive resources. These differ from request logging โ they are business-security events, not infrastructure traces.
Implementation pattern: use IAuthorizationService result inspection in middleware or resource handlers to log AuthorizationFailure. For authentication events, JwtBearerOptions.Events (e.g., OnAuthenticationFailed, OnTokenValidated) allow structured logging at the scheme level.
Critical: do not log claim values like sub, email, or role names to general-purpose application logs โ log aggregators (Datadog, Splunk) are often accessible to a broader team than your security team. Log event types and outcome codes, not raw claim data. Microsoft's guidance: Security logging and monitoring (OWASP A09).
Expert: Multi-Tenancy, Zero Trust, and .NET 10 Security Changes
How do you enforce per-tenant data isolation at the authorization layer in a multi-tenant ASP.NET Core API?
Per-tenant isolation should be enforced at the authorization layer (not just the data access layer) to prevent application bugs from accidentally cross-contaminating tenant data. The pattern: extract the tenant ID from the authenticated user's claims, inject it into a scoped ITenantContext, and use a custom IAuthorizationHandler that validates the requested resource belongs to that tenant.
EF Core Global Query Filters complement this at the persistence layer โ but they are a defence-in-depth measure, not the primary authorization control. Relying solely on query filters means a missing AsNoTracking() or a manual query bypasses the filter. See Multi-Tenant Data Isolation in ASP.NET Core: Row-Level vs Schema vs Database-per-Tenant for the broader architecture decision.
What are the security implications of using IOptionsSnapshot vs IOptionsMonitor for sensitive configuration?
Both support hot-reload of configuration, which is problematic for security-sensitive values like API keys, connection strings, and signing keys. A hot-reloaded signing key can cause in-flight JWT validations to fail if the key changes between token issuance and validation in the same request window.
For security-sensitive configuration, IOptions<T> (snapshot at startup, no hot-reload) is safer. If hot-reload is genuinely needed (e.g., rotating a key without restarting), you must handle the transition window explicitly โ maintain both old and new keys in the validator's ValidIssuingKeys list during rotation.
How does ASP.NET Core 10 change the handling of security headers and HTTPS enforcement compared to earlier versions?
.NET 10 and ASP.NET Core 10 continue to refine the UseHsts() and UseHttpsRedirection() defaults. In development, HTTPS redirection is disabled by default to simplify local development. HSTS is configured with IncludeSubDomains and a MaxAge of 30 days by default โ senior developers should increase this to 1 year for production and consider Preload submission for high-value domains.
New in the .NET 10 cycle: improved OpenAPI security scheme documentation via the built-in OpenAPI support (Microsoft.AspNetCore.OpenApi), allowing security requirements to be declared on endpoint groups โ eliminating the boilerplate of per-endpoint security declarations that was required in Swashbuckle-based setups.
What is the zero-trust model and how does it apply to ASP.NET Core service-to-service communication?
Zero trust means "never trust, always verify" โ no network location is inherently trusted. For service-to-service communication in a microservices architecture, this means every inter-service call must carry a verifiable identity (service account token, mTLS certificate) rather than relying on network-level trust (same VPC, same subnet).
In ASP.NET Core, this is implemented through: mutual TLS (mTLS) using Kestrel's client certificate authentication, or bearer tokens issued by an internal identity provider (e.g., Duende IdentityServer, Azure AD Managed Identities). The calling service acquires a token (typically via client credentials flow), and the receiving service validates it through the normal JWT bearer middleware.
The practical implication: even services behind a private load balancer should require and validate caller identity. Network-level firewalls are a perimeter control, not a zero-trust control.
โ Prefer a one-time tip? Buy us a coffee โ every bit helps keep the content coming!
How to Answer Security Questions in a Senior .NET Interview
Beyond knowing the answers, senior candidates distinguish themselves with how they frame responses:
- Lead with the risk, then the mitigation. Interviewers want to know you understand why a control exists, not just how to configure it.
- Mention trade-offs. Every security control has a usability or performance cost. Acknowledging these shows architectural maturity.
- Reference production experience. Concrete examples ("in a multi-tenant SaaS we secured, we used...") carry more weight than abstract recitations.
- Connect the dots. Security is cross-cutting โ link authentication to logging, link authorization to multi-tenancy, link secrets management to configuration. Senior engineers think in systems.
Frequently Asked Questions
What ASP.NET Core security topics are most commonly asked in senior .NET interviews? JWT authentication and refresh token rotation, OWASP Top 10 mitigations in .NET, policy-based and resource-based authorization, ASP.NET Core Data Protection, CORS misconfiguration, and secrets management strategies are the most consistently tested areas for senior roles. Most interviews include at least one scenario-based question where you are asked to design the authorization model for a specific feature.
What is the difference between [Authorize] and [AllowAnonymous] in ASP.NET Core?
[Authorize] applies the default authorization policy (or a named policy when specified) to an endpoint, requiring the caller to be authenticated and meet any defined requirements. [AllowAnonymous] explicitly bypasses all authorization checks, including any fallback policy. It takes precedence over [Authorize] โ if both are present on the same endpoint, [AllowAnonymous] wins.
How do you protect ASP.NET Core APIs from SQL injection in 2026?
EF Core uses parameterized queries by default for all LINQ-based queries, making traditional SQL injection impossible through the ORM layer. Raw SQL via FromSqlRaw is vulnerable if user input is string-interpolated โ always use FromSqlInterpolated (which uses FormattableString and parameterizes automatically) or DbParameter objects. Dapper requires explicit parameterization โ pass anonymous objects as parameters, never string-concatenate user input into query strings.
What is the recommended way to store passwords in an ASP.NET Core application?
ASP.NET Core Identity's PasswordHasher<TUser> uses PBKDF2 with HMAC-SHA512, 600,000 iterations, and a random salt by default in .NET 8+. Do not use MD5, SHA-1, or SHA-256 without key stretching โ they are too fast for password hashing. If you are not using Identity, use a library like BCrypt.Net-Next or Argon2 via Isopoh.Cryptography.Argon2. Never store plaintext passwords or reversibly encrypted passwords.
What is the significance of ValidateIssuerSigningKey, ValidateIssuer, and ValidateAudience in JWT validation?
These are JWT validation parameters in TokenValidationParameters. ValidateIssuerSigningKey verifies the token's signature against the trusted signing key โ disabling this effectively accepts any token without integrity verification. ValidateIssuer ensures the token was issued by a trusted authority (the iss claim). ValidateAudience ensures the token was intended for your API (the aud claim). All three should be true in production. A common misconfiguration: disabling audience validation in a microservices setup, which allows a token issued for Service A to be replayed against Service B.
How should HTTPS be enforced in an ASP.NET Core production deployment?
Use UseHttpsRedirection() to redirect HTTP requests to HTTPS, and UseHsts() to set the Strict-Transport-Security header, instructing browsers to only connect over HTTPS for the duration of the max-age period. For production, set max-age to at least one year and consider adding includeSubDomains and preload. TLS termination at the load balancer (with only internal traffic being plain HTTP) is acceptable โ in this case, HTTPS redirection can be disabled internally, but HSTS should still be set on responses leaving the load balancer.
What is an authorization policy in ASP.NET Core and how do you register a custom one?
An authorization policy is a named set of requirements that an authenticated caller must satisfy. Policies are registered in AddAuthorization(options => options.AddPolicy("PolicyName", policy => ...)). Common policy builders: RequireAuthenticatedUser(), RequireClaim(claimType, value), RequireRole(role), and custom Requirements added via policy.AddRequirements(new MyRequirement()). Custom requirements need a corresponding AuthorizationHandler<MyRequirement> registered in DI. Named policies are then applied via [Authorize(Policy = "PolicyName")] or RequireAuthorization("PolicyName") on Minimal API endpoints.






