Claude
Skills
Sign in
Back

dotnet-security-owasp

Included with Lifetime
$97 forever

Securing .NET code or reviewing for vulnerabilities. OWASP Top 10 mitigations, pattern warnings.

Security

What this skill does


# dotnet-security-owasp

OWASP Top 10 (2021) security guidance for .NET applications. Each category includes the vulnerability description, .NET-specific risk, mitigation code examples, and common pitfalls. This skill is the canonical owner of deprecated security pattern warnings (CAS, APTCA, .NET Remoting, DCOM, BinaryFormatter).

**Out of scope:** Authentication/authorization implementation -- API-level auth patterns (Identity, OAuth/OIDC, JWT, passkeys, CORS) -- see [skill:dotnet-api-security]. Blazor auth UI (AuthorizeView, CascadingAuthenticationState) -- see [skill:dotnet-blazor-auth]. Cloud-specific security services (Azure Key Vault, AWS Secrets Manager) -- cloud epics. Cryptographic algorithm selection and key management -- see [skill:dotnet-cryptography]. Configuration binding and Options pattern -- see [skill:dotnet-csharp-configuration].

Cross-references: [skill:dotnet-secrets-management] for secrets handling, [skill:dotnet-cryptography] for cryptographic best practices, [skill:dotnet-csharp-coding-standards] for secure coding conventions.

---

## A01: Broken Access Control

**Vulnerability:** Users act outside their intended permissions -- accessing other users' data, elevating privileges, or bypassing access checks.

**Risk in .NET:** Missing `[Authorize]` attributes on controllers/endpoints, insecure direct object references (IDOR) where user IDs are taken from route parameters without ownership validation, and CORS misconfiguration allowing unintended origins.

### Mitigation

```csharp
// 1. Apply authorization globally, then opt out explicitly
builder.Services.AddAuthorizationBuilder()
    .SetFallbackPolicy(new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build());

var app = builder.Build();
app.MapControllers(); // All endpoints require auth by default

// 2. Resource-based authorization to prevent IDOR
public sealed class DocumentAuthorizationHandler
    : AuthorizationHandler<EditRequirement, Document>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        EditRequirement requirement,
        Document resource)
    {
        if (resource.OwnerId == context.User.FindFirstValue(ClaimTypes.NameIdentifier))
        {
            context.Succeed(requirement);
        }
        return Task.CompletedTask;
    }
}

// In the endpoint:
app.MapPut("/documents/{id}", async (
    int id,
    DocumentDto dto,
    IAuthorizationService authService,
    ClaimsPrincipal user,
    AppDbContext db) =>
{
    var document = await db.Documents.FindAsync(id);
    if (document is null) return Results.NotFound();

    var authResult = await authService.AuthorizeAsync(user, document, "Edit");
    if (!authResult.Succeeded) return Results.Forbid();

    document.Title = dto.Title;
    await db.SaveChangesAsync();
    return Results.NoContent();
});
```

```csharp
// 3. Restrict CORS to known origins
builder.Services.AddCors(options =>
{
    options.AddPolicy("Strict", policy =>
    {
        policy.WithOrigins("https://app.example.com")
              .WithMethods("GET", "POST")
              .WithHeaders("Content-Type", "Authorization");
    });
});
```

**Gotcha:** `AllowAnyOrigin()` combined with `AllowCredentials()` is rejected at runtime by ASP.NET Core, but `SetIsOriginAllowed(_ => true)` with `AllowCredentials()` silently allows all origins -- never use this pattern.

---

## A02: Cryptographic Failures

**Vulnerability:** Sensitive data exposed due to weak or missing encryption -- plaintext storage, deprecated algorithms, or improper key management.

**Risk in .NET:** Using MD5/SHA1 for hashing passwords, storing connection strings with plaintext passwords in `appsettings.json`, transmitting sensitive data over HTTP, or using `DES`/`RC2` for encryption.

### Mitigation

```csharp
// Enforce HTTPS and HSTS
builder.Services.AddHttpsRedirection(options =>
{
    options.HttpsPort = 443;
});

var app = builder.Build();
app.UseHsts(); // Strict-Transport-Security header
app.UseHttpsRedirection();

// Never store secrets in appsettings.json -- use user secrets or env vars
// See [skill:dotnet-secrets-management] for proper secrets handling
```

```csharp
// Use Data Protection API for symmetric encryption of application data
public sealed class TokenProtector(IDataProtectionProvider provider)
{
    private readonly IDataProtector _protector =
        provider.CreateProtector("Tokens.V1");

    public string Protect(string plaintext) => _protector.Protect(plaintext);

    public string Unprotect(string ciphertext) => _protector.Unprotect(ciphertext);
}
```

See [skill:dotnet-cryptography] for algorithm selection (AES-GCM, RSA, ECDSA) and key derivation.

---

## A03: Injection

**Vulnerability:** Untrusted data sent to an interpreter as part of a command or query -- SQL injection, command injection, LDAP injection, and cross-site scripting (XSS).

**Risk in .NET:** String concatenation in SQL queries, `Process.Start` with unsanitized input, rendering user input as raw HTML in Razor pages.

### Mitigation

```csharp
// SQL injection prevention: always use parameterized queries
// EF Core is parameterized by default via LINQ
var orders = await db.Orders
    .Where(o => o.CustomerId == customerId)
    .ToListAsync();

// When raw SQL is needed, use parameterized interpolation
var results = await db.Orders
    .FromSqlInterpolated($"SELECT * FROM Orders WHERE Status = {status}")
    .ToListAsync();

// NEVER concatenate user input into SQL:
// var bad = db.Orders.FromSqlRaw("SELECT * FROM Orders WHERE Status = '" + status + "'");
```

```csharp
// XSS prevention: Razor encodes output by default.
// Use @Html.Raw() ONLY for trusted, pre-sanitized HTML.
// In Minimal APIs, return typed results -- not raw strings:
app.MapGet("/greeting", (string name) =>
    Results.Content($"<p>Hello, {HtmlEncoder.Default.Encode(name)}</p>",
        "text/html"));

// Command injection prevention: avoid Process.Start with user input.
// If unavoidable, validate against an allowlist:
public static bool IsAllowedTool(string toolName) =>
    toolName is "dotnet" or "git" or "nuget";
```

**Gotcha:** `FromSqlRaw` with string concatenation bypasses parameterization. Always use `FromSqlInterpolated` or pass `SqlParameter` objects to `FromSqlRaw`.

---

## A04: Insecure Design

**Vulnerability:** Flaws in design patterns that cannot be fixed by implementation alone -- missing rate limiting, lack of defense in depth, unrestricted resource consumption.

**Risk in .NET:** APIs without rate limiting, unbounded file uploads, missing anti-forgery tokens on state-changing operations.

### Mitigation

```csharp
// Rate limiting with built-in middleware (.NET 7+)
builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("api", limiterOptions =>
    {
        limiterOptions.PermitLimit = 100;
        limiterOptions.Window = TimeSpan.FromMinutes(1);
        limiterOptions.QueueLimit = 0;
    });
    options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
});

var app = builder.Build();
app.UseRateLimiter();

app.MapGet("/api/data", () => Results.Ok("data"))
    .RequireRateLimiting("api");
```

```csharp
// Anti-forgery for Minimal APIs (.NET 8+)
builder.Services.AddAntiforgery();

var app = builder.Build();
app.UseAntiforgery();

// Form-bound endpoint: antiforgery validated automatically
app.MapPost("/orders", async ([FromForm] string productId, AppDbContext db) =>
{
    var order = new Order { ProductId = productId };
    db.Orders.Add(order);
    await db.SaveChangesAsync();
    return Results.Created($"/orders/{order.Id}", order);
});

// JSON endpoint: opt in explicitly with RequireAntiforgery()
app.MapPost("/api/orders", async (CreateOrderDto dto, AppDbContext db) =>
{
    var order = new Order { ProductId = dto.ProductId };
    db.Orders.Add(order);
    await db.SaveChangesAsync();
    return Results.Created($"/api/orders/{order.Id}", order);
}).RequireAntiforgery();
``

Related in Security