security-review-dotnetlisted
Install: claude install-skill Sarmkadan/dotnet-senior-skills
# Security Review (.NET)
## Authentication is not authorization
Authentication answers "who is this"; authorization answers "may THEY do THIS to THAT resource". The standard failure: an endpoint behind `[Authorize]` that never checks the resource belongs to the caller.
- Every controller/endpoint group has an explicit auth posture. Set the fallback policy so unmarked endpoints are DENIED, and make anonymous access an explicit opt-in:
```csharp
builder.Services.AddAuthorizationBuilder()
.SetFallbackPolicy(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
```
- Role checks (`[Authorize(Roles = "Admin")]`) cover class-level access; resource-level access (this order, this document) requires a resource check in the handler - `IAuthorizationService.AuthorizeAsync(user, order, "OrderOwner")` or an ownership filter in the query itself.
## IDOR - the highest-yield finding in CRUD APIs
```csharp
// WRONG: any authenticated user reads any invoice by iterating ids
[HttpGet("{id}")]
public Task<InvoiceDto?> Get(int id) => _db.Invoices.Where(i => i.Id == id).ProjectToDto().FirstOrDefaultAsync();
// RIGHT: ownership is part of the query, absence is 404 (don't leak existence via 403)
public Task<InvoiceDto?> Get(int id) => _db.Invoices
.Where(i => i.Id == id && i.CustomerId == _currentUser.CustomerId)
.ProjectToDto().FirstOrDefaultAsync();
```
Review every endpoint taking an id: where is the tenancy/ownership predicate? In multi-tenant systems, prefer