Concepts
What you will learn: When a user requests an invoice, first identify the caller, then check whether they may see that invoice. Connect threats to controls—from sign-in and permissions to injection and secret exposure—and learn what each control does and does not protect.
Before you start: A request carries input from a client that is not trusted automatically. Authentication establishes identity; authorization decides whether an action is allowed. protects data in transit, but does not prove every request is authorized. Basic and endpoint knowledge is enough.
Select a term with a dotted underline for its meaning, then continue reading from the same place.
#Authentication, authorization, cookies and tokens
- Ahmed signs in successfully. That establishes his identity under the sign-in mechanism, not ownership of every order.
- He requests order 42 belonging to Mona. Authorization must check ownership or an explicit permission even after authentication succeeds.
- A cookie or carries identity context according to the design; it does not remove resource-level permission checks. Test a signed-in user trying to access another user's data.
A signed-in caller is not automatically allowed to read every invoice. Authentication establishes identity; authorization decides access to an operation and resource. normally return 401 for missing or invalid authentication and 403 for insufficient permission; sometimes 404 avoids revealing a resource's existence.
| Choice | Behavior and risks |
|---|---|
| Cookie | Browsers attach it automatically; useful for web sessions with HttpOnly, Secure, SameSite and protection. |
| Bearer token in Authorization | The client adds it explicitly; useful for APIs. Theft enables use until expiry or enforced revocation. |
| BFF | Tokens remain on the backend and the browser receives a session cookie; limits JavaScript exposure but still requires CSRF and defenses. |
A cookie is a transport and storage mechanism; is a token format. They are not opposites. HttpOnly blocks JavaScript from reading a cookie but cannot prevent XSS from making authenticated requests.
#OAuth 2.0, OIDC and current flows
access using an access ; it is not a login protocol by itself. adds user authentication and an ID token for the client. APIs accept their intended access tokens, rather than ID tokens issued to the login client.
- The client generates a random code verifier, an S256 challenge and state binding the request to its response.
- The user signs in at the identity provider using an exactly registered redirect ; OIDC uses nonce to bind the ID token to the request.
- A short-lived authorization code returns and is exchanged with the verifier at the token endpoint.
- The client validates the OIDC response and ID token, then calls the API with the access token. Refresh-token issuance depends on scope and provider policy.
addresses code interception; it does not replace state or redirect validation. Confidential clients still authenticate themselves. Client credentials represents a service without a user; prefer managed identity or certificates when supported.
| Older approach | Current choice |
|---|---|
| Implicit: tokens in the authorization response | Authorization code with PKCE reduces exposure and replay risk |
| Password grant: the client collects the user's password | Redirect to the provider; the security BCP prohibits the password grant |
#JWT validation and refresh-token rotation
- A contains a role claim of admin. Decoding it merely reveals the text; it does not prove a trusted issuer created it.
- Validate the signature, issuer, intended audience and lifetime using trusted configuration. Editing a signed role claim must make validation fail.
- Signing does not hide claims, so a signed is not a place to assume secrets are encrypted. Acceptance still needs operation- and resource-specific authorization.
A typical signed JWT contains a header, payload and signature. Base64Url is not encryption; claims must not contain secrets. Validate using a trusted issuer's keys and accepted algorithms, rather than merely decoding or trusting an attacker-controlled alg header.
// Package: Microsoft.AspNetCore.Authentication.JwtBearer 10.x
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://identity.example.com"; // (1)
options.Audience = "orders-api";
options.MapInboundClaims = false;
options.TokenValidationParameters = new()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
RequireSignedTokens = true,
ValidAlgorithms = [SecurityAlgorithms.RsaSha256], // (2)
ClockSkew = TimeSpan.FromSeconds(30)
};
});
builder.Services.AddAuthorization();
// After builder.Build():
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/orders", () => Results.Ok()).RequireAuthorization(); // (3)
- (1) Authority comes from trusted configuration; metadata and signing keys support provider key rotation.
- (2) This example assumes an RS256 provider; match the actual algorithm contract and reject unsigned tokens.
- (3) establishes User before permission evaluation. This endpoint requires login only; order ownership needs further authorization.
Short-lived access tokens limit theft exposure, without one universal lifetime. Offline validation cannot instantly observe revocation; denylists or introspection add state and cost. Refresh rotation atomically consumes a token and issues its successor, detects reuse and revokes a family under provider policy. Coordinate parallel refreshes so legitimate retries are handled deliberately. See JWT bearer configuration.
#ASP.NET Core Identity and .NET 10 passkeys
Identity manages users, password hashing, roles, lockout, email confirmation and recovery . It is not a complete OAuth authorization server. Its endpoint bearer tokens are not necessarily JWTs and are not a general replacement for an provider.
10 adds Identity passkey support using WebAuthn. Registration stores a public key; login signs a challenge bound to the relying party and origin. A biometric or PIN authorizes local key use; the server receives neither biometric data nor the private key.
- Issue a fresh, short-lived challenge bound to the operation and user where applicable.
- The browser and authenticator perform the WebAuthn ceremony.
- Verify challenge, origin, RP ID, signature and required user verification.
- Provide credential management, device-loss handling and appropriately secure recovery.
| Before | ASP.NET Core 10 |
|---|---|
| Custom WebAuthn integration or a separate library | Identity passkey APIs and application-specific integration |
Origin binding resists phishing, but weak recovery can undermine it. Some credentials synchronize through passkey managers, so do not assume every private key is confined to one physical device. See Identity passkeys.
#Roles, claims, policies and resource authorization
A role such as Finance is a broad classification. A claim is an assertion from a trusted issuer. Policies combine requirements, and handlers evaluate them. Resource-based authorization fits decisions involving an order's owner and state, beyond caller identity alone.
builder.Services.AddAuthorizationBuilder()
.AddPolicy("orders.edit", policy => policy
.RequireAuthenticatedUser()
.RequireClaim("permission", "orders.edit")); // (1)
// Endpoint fragment: tenant and user IDs come from validated identity context.
var order = await db.Orders.SingleOrDefaultAsync(
o => o.Id == id && o.TenantId == tenantId, ct); // (2)
if (order is null) return Results.NotFound();
var decision = await authorization.AuthorizeAsync(
user, order, "EditOwnPendingOrder"); // (3)
if (!decision.Succeeded) return Results.Forbid();
// Perform the update with concurrency protection.
- (1) Claims require a trusted issuer; the policy checks the required value.
- (2) Tenant filtering happens before returning data; unguessable GUIDs are not authorization.
- (3) Register this custom policy with a requirement and handler that checks ownership and Pending status and calls Succeed on approval. An unregistered policy cannot execute.
This fragment omits the handler implementation. Also require the endpoint permission policy and enforce tenant boundaries in reads, writes, jobs and exports. filters provide defense in depth, but raw , IgnoreQueryFilters and administrative paths still need review. Cache keys, blob paths and message handlers also require tenant . See resource authorization.
#OWASP Top 10:2025 mapped to .NET
This is risk awareness, rather than certification of application security. This table uses 2025; is no longer a standalone category as in 2021, but remains a threat to mitigate.
| Category | application response |
|---|---|
| A01 Broken Access Control | Deny by default, policies, ownership and tenant checks, SSRF prevention. |
| A02 Security Misconfiguration | No production exception detail leaks, narrow , secure headers and configuration. |
| A03 Software Supply Chain Failures | Trusted package sources, dependency scans and protected build pipelines. |
| A04 Cryptographic Failures | , password hashing, key management and authenticated encryption. |
| A05 Injection | Parameterized queries, output encoding and no execution of input as commands. |
| A06 Insecure Design | Threat modeling, quantity and amount limits, abuse-case tests. |
| A07 Authentication Failures | Trusted Identity or provider integration, or passkeys, rate limits and secure recovery. |
| A08 Software or Data Integrity Failures | Verify webhook signatures and artifact provenance; avoid unsafe . |
| A09 Security Logging and Alerting Failures | Sensitive-action auditing with alerts, retention and secret redaction. |
| A10 Mishandling of Exceptional Conditions | Fail closed, bound resources and timeouts, clean up without bypassing authorization. |
Category names follow OWASP Top 10:2025.
#SQL injection: separate data from executable syntax
Concatenating a username into lets input alter query meaning. Validation does not replace parameters. normally parameterizes values, while concatenated raw SQL remains dangerous.
var sql = $"SELECT * FROM Users WHERE Email = '{email}'";
var users = await db.Users.FromSqlRaw(sql).ToListAsync(ct);var users = await db.Users
.Where(user => user.Email == email)
.ToListAsync(ct); // (1)
// If raw SQL is required, pass interpolation directly to FromSql:
var matches = await db.Users
.FromSql($"SELECT * FROM Users WHERE Email = {email}") // (2)
.ToListAsync(ct);- (1) The value travels as a parameter, so quotes inside it do not become SQL commands.
- (2) FromSql receives a FormattableString directly; do not first turn it into a string for FromSqlRaw. Column names and sort directions are syntax, so select them from a fixed allowlist.
Use a least-privileged database account. Apply the same separation to shell execution: prefer and validated argument lists over constructed command strings.
#XSS and CSRF: different attacks
runs attacker script in your origin, exposing data and authenticated actions. Razor -encodes by default, but Html.Raw with user content or DOM innerHTML is dangerous. Rich HTML requires a maintained sanitizer, and JavaScript or contexts need their own encoding rules.
@Html.Raw(Model.CustomerNote)@Model.CustomerNoteexploits automatic cookie attachment when a victim visits an attacker page. Validate antiforgery on state-changing cookie-authenticated requests, with SameSite as another layer. GET must not mutate state. is not a complete CSRF defense.
// MVC cookie-authenticated app; register before Build.
builder.Services.AddControllersWithViews(options =>
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute())); // (1)
// Razor form: the form tag helper normally generates this token already.
// Explicit generation is shown to make the mechanism visible.
// @Html.AntiForgeryToken() // (2)
- (1) The filter validates unsafe methods; it requires Microsoft.AspNetCore.Mvc. Authentication and authorization still apply separately.
- (2) Forms submit a request token paired with a cookie token; AJAX uses a configured header. Minimal antiforgery behavior depends on binding and configuration; MVC filters do not cover it.
Content-Security-Policy adds XSS defense without replacing encoding. Explicitly attached bearer headers reduce conventional CSRF exposure, but putting that token in a cookie restores automatic attachment. See ASP.NET Core antiforgery guidance.
#SSRF: the server must not proxy arbitrary URLs
A user-supplied image can target cloud metadata or internal services. Checking an https prefix is insufficient: redirects, rebinding, IPv6 and alternate formats create other routes.
var bytes = await http.GetByteArrayAsync(userSuppliedUrl, ct);builder.Services.AddHttpClient("product-images", client =>
client.BaseAddress = new Uri("https://images.example.com/"))
.ConfigurePrimaryHttpMessageHandler(() =>
new HttpClientHandler { AllowAutoRedirect = false }); // (1)
// Endpoint fragment; imageId is a validated Guid, not a path or URL.
var client = clients.CreateClient("product-images");
using var response = await client.GetAsync(
$"products/{imageId:N}", HttpCompletionOption.ResponseHeadersRead, ct); // (2)
response.EnsureSuccessStatusCode();- (1) A fixed, controlled host and disabled redirects prevent forwarding to an unexpected redirect target.
- (2) A typed ID cannot become an absolute URL or traversal path; headers-first enables size and type checks before body consumption.
Add egress firewall or proxy rules against private, loopback, link-local and metadata destinations, DNS controls, timeouts and a hard streamed-body limit even without Content-Length. If arbitrary URLs are a real requirement, validate resolved destinations and each redirect and connection, rather than relying on a superficial host check. See the OWASP SSRF guidance.
#Password hashing, encryption and HMAC
A general hash such as SHA-256 is a one-way fingerprint but is too fast for password storage. Use a slow, randomly salted password hasher with an appropriate work factor, such as Identity or a maintained Argon2id implementation. A salt is not secret: it prevents shared precomputation across users but cannot rescue a weak password.
| Tool | Purpose |
|---|---|
| Hash | A fingerprint; without a secret it does not authenticate the source. |
| Encryption | Recover plaintext using a key; use authenticated encryption from maintained libraries. |
| Integrity and authenticity between shared-secret holders; neither confidentiality nor non-repudiation. |
using Microsoft.AspNetCore.Identity;
var hasher = new PasswordHasher<IdentityUser>();
var storedHash = hasher.HashPassword(user, password); // (1)
var result = hasher.VerifyHashedPassword(user, storedHash, suppliedPassword); // (2)
if (result == PasswordVerificationResult.SuccessRehashNeeded)
{
// Persist a replacement hash through the user store after successful login.
}
- (1) The framework generates a salt and stores format and cost metadata; avoid custom salt management.
- (2) Reject Failed. SuccessRehashNeeded is successful verification requiring an upgrade; application login should use UserManager and SignInManager for lockout, and related policy.
Never log passwords or hashes. Measure work-factor changes on your resources so hashing cannot easily become a resource-exhaustion path.
#Secrets, Data Protection and dependency scanning
Use development user-secrets, which are not an encrypted vault. In production, prefer a vault and workload identity where possible, with least privilege and rotation. Environment variables transport configuration but can still leak through dumps or logs. Revoke and replace committed secrets immediately; deleting the file is insufficient.
Data Protection protects application state with encryption, integrity, purpose and key rotation. Instances sharing cookies need the same application discriminator and persistent key ring, protected at rest. It is distinct from Windows DPAPI and is not automatically a KMS replacement for long-lived archives.
using Microsoft.AspNetCore.DataProtection;
builder.Services.AddDataProtection().SetApplicationName("Shop"); // (1)
// Production: configure persistent shared keys and encryption at rest.
public sealed class ProtectedState(IDataProtectionProvider provider)
{
private readonly IDataProtector _protector =
provider.CreateProtector("checkout-state.v1"); // (2)
public string Protect(string value) => _protector.Protect(value);
public string Unprotect(string value) => _protector.Unprotect(value);
}
# .NET 10 SDK form; include indirect dependencies too.
dotnet package list --vulnerable --include-transitive
# Older SDK form:
dotnet list package --vulnerable --include-transitive
- (1) The name coordinates application isolation, not key storage. Configure persistence, access control and backup before scaling out.
- (2) A purpose separates payload uses; Unprotect fails on tampering or unavailable keys. Do not expose exception details to clients.
Listing vulnerabilities does not itself implement your failure policy; inspect results or configure NuGet audit severities and warning failures. Review transitive dependencies, package provenance, advisories and upgrade plans, with dated, narrow exceptions. See Data Protection and package-list commands.
#HTTPS, security headers and webhook verification
protects transport. instructs browsers to use HTTPS on subsequent visits under its policy; it neither replaces nor protects the first visit without preload. Behind a proxy, configure trusted forwarded headers before HTTPS redirection. Use source restrictions, frame-ancestors, X-Content-Type-Options: nosniff and an appropriate Referrer-Policy; test rather than blindly copying headers.
A webhook is not trustworthy merely because it says payment succeeded. Verify the signature over raw bytes before parsing, authenticate a timestamp under the provider's protocol to limit replay, then validate order, amount, currency and .
using System.Security.Cryptography;
// Illustrative provider contract: hex HMAC-SHA256 over the exact raw body.
static bool Verify(byte[] rawBody, string signatureHex, byte[] secret)
{
if (signatureHex.Length != 64) return false; // (1)
byte[] supplied;
try { supplied = Convert.FromHexString(signatureHex); }
catch (FormatException) { return false; }
byte[] expected = HMACSHA256.HashData(secret, rawBody); // (2)
return CryptographicOperations.FixedTimeEquals(expected, supplied); // (3)
}
- (1) Reject malformed input without leaking exception details; bound the request body before buffering.
- (2) Compute over exact bytes; reserialization changes the signature. If the provider signs timestamp plus body, follow that precise format rather than this abbreviated contract.
- (3) Compare valid-length values in constant time; TLS remains necessary because does not encrypt.
This verifier alone does not prevent replay. Authenticate a timestamp, enforce a time window and persist a unique event ID atomically with the effect or inbox. Compare amount and currency against authoritative order data, and never treat a browser redirect as proof of payment.
Interview questions
No results
Check your reasoning
Why does this choice fit? When would it fail? What changes under higher load or a dependency failure?
19.1What is the difference between authentication and authorization?Junior
I use authentication to establish who is calling and authorization to decide what that identity may do. A valid login does not imply access to every order. In an I normally challenge missing or invalid credentials with 401 and reject insufficient permission with 403, while sometimes using 404 to avoid revealing resource existence. In I establish the principal before applying endpoint policies and perform ownership and tenant checks when the decision depends on a specific resource.
19.2Are cookies and JWTs competing authentication protocols?Junior
I separate format from transport. is a format, while a cookie is a browser storage and delivery mechanism; a cookie can carry different session representations. Cookies are automatically attached and need defenses, while a bearer token in an Authorization header is explicitly attached but must be protected from theft. For browser applications I often consider a backend-for-frontend with HttpOnly secure session cookies, keeping provider tokens on the server while still defending against and CSRF.
19.3How do OAuth 2.0 and OpenID Connect differ?Mid
I use for delegated access and OpenID Connect for user authentication on top of that framework. The access is intended for the API, while the ID token tells the client about the authenticated user. I do not send an ID token to an API as a substitute for its access token. I use a maintained provider and client , validate the intended audiences and issuers, and request only the scopes and claims the application needs.
19.4Explain authorization code with PKCE and client credentials.Mid
For an interactive user, I use authorization code with : the client sends a challenge and later proves possession of the verifier while exchanging the returned code. That limits code interception, while state, nonce and exact redirect registration address other response-binding risks. A confidential client still authenticates itself. For machine-to-machine access without a user, I use client credentials or a platform workload identity. I never embed a confidential client secret inside a public browser or mobile application.
19.5Why avoid implicit and password grants?Mid
I avoid implicit flow because returning access through the authorization response increases leakage and replay exposure. Authorization code with provides a better exchange model. I also reject the password grant, which requires the client to collect the user's password and does not fit modern authentication such as passkeys and provider-managed . The OAuth security best current practice prohibits that grant. I redirect users to a trusted provider instead of building a new password-collection flow into each application.
19.6Is a JWT encrypted, and what must an API validate?Junior
I treat the usual signed as readable data, not encrypted storage. Its header and payload are Base64Url-encoded, and the signature protects integrity under a trusted key. The validates the signature, accepted algorithms, issuer, audience and lifetime with an intentional clock-skew allowance. I obtain keys through trusted provider metadata and support rotation rather than accepting arbitrary -supplied keys. Decoding a token is only inspection; it does not establish that the claims are trustworthy.
19.7How do refresh-token rotation and access-token revocation work?Senior
I keep access- lifetimes bounded and rotate refresh tokens through an atomic consume-and-replace operation. Reuse detection can revoke the token family under the provider's policy, with deliberate handling of concurrent refresh attempts. Revoking a refresh token prevents future renewal but does not instantly invalidate an already issued access token in an offline-validating . Immediate revocation needs an additional mechanism, such as introspection or a denylist, which adds , state and availability trade-offs.
19.8What do ASP.NET Core Identity and passkeys provide?Mid
I use Identity for account management, password hashing, roles, lockout and related login facilities, rather than treating it as a complete OAuth authorization server. 10 adds WebAuthn passkey support. The server stores a public key and verifies a fresh challenge signed by the user's authenticator, with origin and relying-party binding. Biometrics authorize local key use and are not sent to the server. I also design credential recovery carefully because weak recovery can undermine strong login.
19.9How do roles, claims, policies and resource authorization relate?Mid
I use roles for broad classifications and claims for trusted assertions such as permissions or tenant identity. Policies express requirements without scattering checks across endpoints. When permission depends on the specific object, I use resource-based authorization through a handler or an explicitly scoped query. For example, editing an order can require both an edit permission and ownership while the order is pending. I enforce the same boundary in background jobs and exports rather than relying only on controller attributes.
19.10How do you prevent cross-tenant data leakage?Senior
I establish tenant identity from a trusted authenticated context and authorize any tenant selection. Queries and writes remain tenant-scoped, with resource checks where needed. I extend to cache keys, storage paths, exports and message handlers. Database filters help, but raw , administrative bypasses and unfiltered relationships still require review. I add tests where one tenant supplies another tenant's identifiers, including update and bulk paths, because an unpredictable identifier alone is not an access-control boundary.
19.11How do you prevent SQL injection in .NET?Junior
I keep values separate from syntax using parameters, usually through or a parameterized command. If I use EF FromSql, I pass the interpolated expression directly so it remains parameterized instead of building a string for FromSqlRaw. Parameters cannot represent identifiers such as column names, so dynamic sorting uses a fixed allowlist. Input validation and a least-privileged database account add protection, but neither replaces correct parameterization.
19.12What is XSS and how do you mitigate it?Mid
I treat as attacker-controlled script execution in the application's origin. I use context-appropriate encoding, keep Razor's default encoding and avoid unsafe sinks such as Html.Raw or innerHTML for untrusted content. Rich needs a maintained sanitizer, and a restrictive Content Security Policy adds defense in depth. HttpOnly cookies reduce direct reading but do not stop malicious script from issuing authenticated requests. I therefore protect the rendering path rather than relying on cookie flags alone.
19.13What is CSRF and does CORS prevent it?Mid
I consider when the browser automatically attaches credentials, especially session cookies. An attacker can cause a victim's browser to submit a state-changing request without reading its response. I validate antiforgery , use suitable SameSite settings and avoid state changes through GET. governs cross-origin browser access but is not a complete CSRF defense, since some requests can still be sent. Explicit bearer headers change the threat model, while storing that bearer token in a cookie restores automatic attachment.
19.14How do you defend against SSRF?Senior
I avoid accepting arbitrary URLs when a typed identifier for a fixed service will meet the requirement. For outgoing fetches I constrain destinations, disable or revalidate redirects and enforce network egress rules against internal and metadata addresses. I account for rebinding, IPv6 and resolved addresses rather than checking only the text. Timeouts, response-size limits and bounded concurrency also matter. When arbitrary fetching is unavoidable, I isolate it behind a deliberately restricted proxy or service boundary.
19.15How should passwords be stored?Junior
I store passwords with a maintained, salted password-hashing implementation such as Identity's hasher, with an appropriate work factor. I do not store plaintext, reversible encryption or a fast general hash such as bare SHA-256. The stored format includes the information needed for verification and upgrades. I use the account framework for lockout and , never log passwords or hashes, and measure hashing cost so stronger settings remain sustainable under login traffic and abuse.
19.16How do hashing, encryption, HMAC and Data Protection differ?Mid
I use a hash for a one-way fingerprint, encryption when authorized code must recover the original data, and to authenticate data shared between secret holders. A plain hash does not authenticate the sender, and HMAC does not encrypt. Data Protection packages encryption, integrity, purposes and key rotation for application state such as cookies. In a multi-instance deployment I configure persistent shared keys and protection at rest, and I do not assume it replaces every long-term encryption requirement.
19.17How do you verify a payment webhook safely?Senior
I verify the provider's exact signature contract over the raw request bytes before , with bounded body size and constant-time comparison for valid signatures. I validate an authenticated timestamp where the protocol provides one and deduplicate event IDs atomically with processing. Then I compare the order, amount and currency against authoritative data and apply only valid state transitions. remains necessary, and I never treat a client redirect as payment proof. Failed deliveries need monitored retries and reconciliation.
19.18How do you manage secrets and vulnerable dependencies?Senior
I keep secrets out of source control, use development user-secrets locally and prefer a production vault with workload identity and least privilege. Exposed credentials are revoked and rotated immediately. For dependencies I inspect direct and transitive vulnerabilities, package provenance and maintained versions, then enforce an explicit policy rather than assuming a listing command blocks releases. Exceptions have owners and expiry dates. I also protect build credentials and artifact publication, since dependency security includes the supply chain rather than package versions alone.
Test yourself
Quiz answers and completed-topic marks are saved in this browser; they do not sync across devices.
A valid login can read another user's invoice by changing an ID. Which failure?
Which token should an API normally accept from an OIDC client?
What does PKCE primarily protect against?
What is true of a typical signed JWT payload?
Which password storage approach is appropriate?
Why does an HttpOnly cookie not eliminate XSS risk?
Which defense directly addresses cookie-authenticated CSRF?
Why disable redirects when fetching from an allowed external service?
What is stored by the server during passkey registration?
A valid HMAC webhook is replayed tomorrow. What additional control matters?