| layout | default |
|---|---|
| title | Convey.WebApi.Security |
| parent | Authentication & Security |
Web API security extensions providing authentication, authorization, CORS configuration, rate limiting, and security headers for ASP.NET Core applications with integrated security policies and middleware.
dotnet add package Convey.WebApi.SecurityConvey.WebApi.Security provides:
- Authentication integration - JWT, API key, and certificate authentication
- Authorization policies - Role-based and policy-based authorization
- CORS configuration - Cross-Origin Resource Sharing setup
- Rate limiting - Request throttling and rate limiting
- Security headers - HSTS, CSP, and other security headers
- Input validation - Request sanitization and validation
- API versioning security - Version-specific security policies
- Audit logging - Security event logging and monitoring
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddConvey()
.AddWebApi()
.AddWebApiSecurity() // Enables security features
.AddJwt(); // JWT authentication
var app = builder.Build();
app.UseAuthentication()
.UseAuthorization()
.UseSecurityHeaders()
.UseCors()
.UseRateLimiting();
app.Run();var builder = WebApplication.CreateBuilder(args);
// Security configuration
builder.Services.AddConvey()
.AddWebApi()
.AddWebApiSecurity(options =>
{
options.EnableCors = true;
options.EnableRateLimiting = true;
options.EnableSecurityHeaders = true;
options.EnableInputValidation = true;
options.EnableAuditLogging = true;
})
.AddJwt()
.AddSecurityHeaders(headers =>
{
headers.AddDefaultSecurePolicy();
headers.AddContentSecurityPolicy("default-src 'self'; script-src 'self' 'unsafe-inline'");
headers.AddStrictTransportSecurity(maxAge: TimeSpan.FromDays(365));
})
.AddCors(cors =>
{
cors.AddPolicy("ApiPolicy", policy =>
{
policy.WithOrigins("https://localhost:3000", "https://api.example.com")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
})
.AddRateLimiting(options =>
{
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(context =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: context.User?.Identity?.Name ?? context.Connection.RemoteIpAddress?.ToString(),
factory: partition => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 100,
Window = TimeSpan.FromMinutes(1)
}));
});
var app = builder.Build();
// Security middleware pipeline
app.UseSecurityHeaders();
app.UseRateLimiter();
app.UseCors("ApiPolicy");
app.UseAuthentication();
app.UseAuthorization();
app.Run();Multiple authentication schemes:
// JWT Authentication
public class JwtAuthenticationSetup
{
public static void Configure(IServiceCollection services, IConfiguration configuration)
{
services.AddConvey()
.AddJwt(jwt =>
{
jwt.SecretKey = configuration["JWT:SecretKey"];
jwt.Issuer = configuration["JWT:Issuer"];
jwt.Audience = configuration["JWT:Audience"];
jwt.ExpiryMinutes = 60;
jwt.ValidateIssuer = true;
jwt.ValidateAudience = true;
jwt.ValidateLifetime = true;
jwt.ValidateIssuerSigningKey = true;
jwt.ClockSkew = TimeSpan.Zero;
});
}
}
// API Key Authentication
public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationSchemeOptions>
{
private readonly IApiKeyService _apiKeyService;
public ApiKeyAuthenticationHandler(
IOptionsMonitor<ApiKeyAuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
IApiKeyService apiKeyService)
: base(options, logger, encoder, clock)
{
_apiKeyService = apiKeyService;
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.ContainsKey("X-API-Key"))
{
return AuthenticateResult.Fail("API Key header not found");
}
var apiKey = Request.Headers["X-API-Key"].FirstOrDefault();
if (string.IsNullOrEmpty(apiKey))
{
return AuthenticateResult.Fail("API Key is empty");
}
var apiKeyInfo = await _apiKeyService.ValidateApiKeyAsync(apiKey);
if (apiKeyInfo == null)
{
return AuthenticateResult.Fail("Invalid API Key");
}
var claims = new[]
{
new Claim(ClaimTypes.Name, apiKeyInfo.Name),
new Claim(ClaimTypes.NameIdentifier, apiKeyInfo.Id.ToString()),
new Claim("ApiKeyId", apiKeyInfo.Id.ToString()),
new Claim("Scope", apiKeyInfo.Scope)
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
}
// Certificate Authentication
public class CertificateAuthenticationSetup
{
public static void Configure(IServiceCollection services)
{
services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options =>
{
options.AllowedCertificateTypes = CertificateTypes.All;
options.RevocationMode = X509RevocationMode.NoCheck;
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, context.ClientCertificate.Subject),
new Claim(ClaimTypes.Name, context.ClientCertificate.GetNameInfo(X509NameType.SimpleName, false)),
new Claim("thumbprint", context.ClientCertificate.Thumbprint)
};
context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));
context.Success();
return Task.CompletedTask;
}
};
});
}
}
// Multi-scheme authentication
services.AddAuthentication()
.AddJwtBearer("JWT", options => { /* JWT config */ })
.AddScheme<ApiKeyAuthenticationSchemeOptions, ApiKeyAuthenticationHandler>("ApiKey", options => { })
.AddCertificate("Certificate", options => { /* Certificate config */ });
// Authentication endpoints
app.UseEndpoints(endpoints =>
{
// JWT protected endpoint
endpoints.Get("/api/users/profile", async (HttpContext ctx) =>
{
var user = ctx.User;
await ctx.Response.WriteAsJsonAsync(new
{
Id = user.FindFirst(ClaimTypes.NameIdentifier)?.Value,
Name = user.FindFirst(ClaimTypes.Name)?.Value,
Email = user.FindFirst(ClaimTypes.Email)?.Value,
Roles = user.FindAll(ClaimTypes.Role).Select(c => c.Value).ToArray()
});
}).RequireAuthorization("JWT");
// API Key protected endpoint
endpoints.Get("/api/data/export", async (HttpContext ctx) =>
{
var apiKeyId = ctx.User.FindFirst("ApiKeyId")?.Value;
var scope = ctx.User.FindFirst("Scope")?.Value;
// Export logic based on API key scope
await ctx.Response.WriteAsJsonAsync(new { Message = "Data exported", ApiKeyId = apiKeyId });
}).RequireAuthorization("ApiKey");
// Certificate protected endpoint
endpoints.Get("/api/admin/system-info", async (HttpContext ctx) =>
{
var thumbprint = ctx.User.FindFirst("thumbprint")?.Value;
// Admin operations
await ctx.Response.WriteAsJsonAsync(new { SystemInfo = "...", CertThumbprint = thumbprint });
}).RequireAuthorization("Certificate");
});Role-based and policy-based authorization:
// Authorization policies
services.AddAuthorization(options =>
{
// Role-based policies
options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
options.AddPolicy("ManagerOrAdmin", policy => policy.RequireRole("Manager", "Admin"));
// Claims-based policies
options.AddPolicy("CanEditUsers", policy =>
policy.RequireClaim("permission", "users.edit"));
options.AddPolicy("CanViewReports", policy =>
policy.RequireClaim("permission", "reports.view"));
// Custom requirement policies
options.AddPolicy("MinimumAge", policy =>
policy.Requirements.Add(new MinimumAgeRequirement(18)));
options.AddPolicy("SameUser", policy =>
policy.Requirements.Add(new SameUserRequirement()));
// Resource-based policies
options.AddPolicy("DocumentOwner", policy =>
policy.Requirements.Add(new DocumentOwnerRequirement()));
// Time-based policies
options.AddPolicy("BusinessHours", policy =>
policy.Requirements.Add(new BusinessHoursRequirement()));
// IP-based policies
options.AddPolicy("InternalNetwork", policy =>
policy.Requirements.Add(new IpAddressRequirement("192.168.1.0/24")));
});
// Custom authorization requirements
public class MinimumAgeRequirement : IAuthorizationRequirement
{
public int MinimumAge { get; }
public MinimumAgeRequirement(int minimumAge)
{
MinimumAge = minimumAge;
}
}
public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
MinimumAgeRequirement requirement)
{
var dobClaim = context.User.FindFirst("dob");
if (dobClaim != null && DateTime.TryParse(dobClaim.Value, out var dob))
{
var age = DateTime.Today.Year - dob.Year;
if (dob.Date > DateTime.Today.AddYears(-age)) age--;
if (age >= requirement.MinimumAge)
{
context.Succeed(requirement);
}
}
return Task.CompletedTask;
}
}
// Resource-based authorization
public class DocumentOwnerRequirement : IAuthorizationRequirement { }
public class DocumentOwnerHandler : AuthorizationHandler<DocumentOwnerRequirement, Document>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
DocumentOwnerRequirement requirement,
Document resource)
{
var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (resource.OwnerId == userId || context.User.IsInRole("Admin"))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
// Authorization endpoints
app.UseEndpoints(endpoints =>
{
// Role-based authorization
endpoints.Get("/api/admin/users", async (HttpContext ctx) =>
{
// Admin-only endpoint
}).RequireAuthorization("AdminOnly");
// Claims-based authorization
endpoints.Post("/api/users/{id}", async (HttpContext ctx) =>
{
// Requires edit permission
}).RequireAuthorization("CanEditUsers");
// Resource-based authorization
endpoints.Get("/api/documents/{id}", async (Guid id, HttpContext ctx, IAuthorizationService authService) =>
{
var document = await GetDocumentAsync(id);
var authResult = await authService.AuthorizeAsync(ctx.User, document, "DocumentOwner");
if (!authResult.Succeeded)
{
ctx.Response.StatusCode = 403;
return;
}
await ctx.Response.WriteAsJsonAsync(document);
});
// Multiple policy authorization
endpoints.Delete("/api/users/{id}", async (HttpContext ctx) =>
{
// Requires both admin role and business hours
}).RequireAuthorization("AdminOnly", "BusinessHours");
});
// Register authorization handlers
services.AddScoped<IAuthorizationHandler, MinimumAgeHandler>();
services.AddScoped<IAuthorizationHandler, DocumentOwnerHandler>();Cross-Origin Resource Sharing setup:
// CORS configuration
services.AddCors(options =>
{
// Development policy (permissive)
options.AddPolicy("Development", policy =>
{
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
// Production policy (restrictive)
options.AddPolicy("Production", policy =>
{
policy.WithOrigins(
"https://app.example.com",
"https://admin.example.com",
"https://mobile.example.com")
.WithMethods("GET", "POST", "PUT", "DELETE")
.WithHeaders("Content-Type", "Authorization", "X-Requested-With")
.AllowCredentials()
.SetPreflightMaxAge(TimeSpan.FromMinutes(10));
});
// API-specific policy
options.AddPolicy("ApiConsumers", policy =>
{
policy.WithOrigins("https://partner1.com", "https://partner2.com")
.WithMethods("GET", "POST")
.WithHeaders("Content-Type", "Authorization", "X-API-Key")
.WithExposedHeaders("X-Total-Count", "X-Page-Count")
.SetIsOriginAllowedToReturnTrue(); // Dynamic origin validation
});
// Mobile app policy
options.AddPolicy("MobileApps", policy =>
{
policy.WithOrigins("capacitor://localhost", "ionic://localhost")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
// Dynamic CORS policy
services.AddSingleton<ICorsPolicyProvider, DynamicCorsPolicyProvider>();
public class DynamicCorsPolicyProvider : ICorsPolicyProvider
{
private readonly ILogger<DynamicCorsPolicyProvider> _logger;
private readonly IConfiguration _configuration;
public DynamicCorsPolicyProvider(ILogger<DynamicCorsPolicyProvider> logger, IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
}
public Task<CorsPolicy> GetPolicyAsync(HttpContext context, string policyName)
{
var origin = context.Request.Headers["Origin"].ToString();
// Get allowed origins from configuration or database
var allowedOrigins = _configuration.GetSection("Cors:AllowedOrigins").Get<string[]>();
var policy = new CorsPolicyBuilder()
.WithOrigins(allowedOrigins)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
.Build();
_logger.LogInformation("CORS policy applied for origin: {Origin}", origin);
return Task.FromResult(policy);
}
}
// Conditional CORS usage
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseCors("Development");
}
else
{
app.UseCors("Production");
}Request throttling and rate limiting:
// Rate limiting configuration
services.AddRateLimiter(options =>
{
// Global rate limiter
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(context =>
{
var userId = context.User?.Identity?.Name;
var clientId = context.Request.Headers["X-Client-Id"].FirstOrDefault();
var ipAddress = context.Connection.RemoteIpAddress?.ToString();
var partitionKey = userId ?? clientId ?? ipAddress ?? "anonymous";
return RateLimitPartition.GetFixedWindowLimiter(partitionKey, _ => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 100,
Window = TimeSpan.FromMinutes(1)
});
});
// Per-endpoint policies
options.AddPolicy("AuthPolicy", context =>
{
var userId = context.User?.Identity?.Name ?? context.Connection.RemoteIpAddress?.ToString();
return RateLimitPartition.GetTokenBucketLimiter(userId, _ => new TokenBucketRateLimiterOptions
{
TokenLimit = 10,
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
QueueLimit = 5,
ReplenishmentPeriod = TimeSpan.FromSeconds(10),
TokensPerPeriod = 2,
AutoReplenishment = true
});
});
options.AddPolicy("ApiPolicy", context =>
{
var apiKey = context.Request.Headers["X-API-Key"].FirstOrDefault();
return RateLimitPartition.GetSlidingWindowLimiter(apiKey ?? "unknown", _ => new SlidingWindowRateLimiterOptions
{
PermitLimit = 1000,
Window = TimeSpan.FromHours(1),
SegmentsPerWindow = 12,
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
QueueLimit = 10
});
});
options.AddPolicy("UploadPolicy", context =>
{
var userId = context.User?.Identity?.Name ?? "anonymous";
return RateLimitPartition.GetConcurrencyLimiter(userId, _ => new ConcurrencyLimiterOptions
{
PermitLimit = 3,
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
QueueLimit = 5
});
});
// Custom rejection response
options.OnRejected = async (context, token) =>
{
context.HttpContext.Response.StatusCode = 429;
var response = new
{
Error = "Rate limit exceeded",
RetryAfter = context.Lease.GetAllMetadata().FirstOrDefault(m => m.Key == "RETRY_AFTER")?.Value,
Limit = context.Lease.GetAllMetadata().FirstOrDefault(m => m.Key == "LIMIT")?.Value
};
await context.HttpContext.Response.WriteAsJsonAsync(response, cancellationToken: token);
};
});
// Rate limited endpoints
app.UseEndpoints(endpoints =>
{
// Authentication endpoints with stricter limits
endpoints.Post("/api/auth/login", async (HttpContext ctx) =>
{
// Login logic
}).RequireRateLimiting("AuthPolicy");
// API endpoints with standard limits
endpoints.Get("/api/data", async (HttpContext ctx) =>
{
// Data retrieval
}).RequireRateLimiting("ApiPolicy");
// File upload with concurrency limits
endpoints.Post("/api/upload", async (HttpContext ctx) =>
{
// File upload logic
}).RequireRateLimiting("UploadPolicy");
// Global rate limiting (uses global limiter)
endpoints.Get("/api/public/status", async (HttpContext ctx) =>
{
await ctx.Response.WriteAsJsonAsync(new { Status = "OK" });
});
});
app.UseRateLimiter();Implement security headers for protection:
// Security headers configuration
services.AddSecurityHeaders(options =>
{
// Strict Transport Security
options.AddStrictTransportSecurity(maxAge: TimeSpan.FromDays(365), includeSubdomains: true);
// Content Security Policy
options.AddContentSecurityPolicy(builder =>
{
builder.DefaultSources.Self()
.ScriptSources.Self().UnsafeInline().From("https://cdn.jsdelivr.net")
.StyleSources.Self().UnsafeInline().From("https://fonts.googleapis.com")
.FontSources.Self().From("https://fonts.gstatic.com")
.ImageSources.Self().Data().From("https:")
.ConnectSources.Self().From("https://api.example.com")
.FrameAncestors.None()
.BaseUris.Self();
});
// X-Frame-Options
options.AddFrameOptions(FrameOptionsPolicy.Deny);
// X-Content-Type-Options
options.AddContentTypeOptions();
// Referrer Policy
options.AddReferrerPolicy(ReferrerPolicy.StrictOriginWhenCrossOrigin);
// Permissions Policy
options.AddPermissionsPolicy(builder =>
{
builder.Camera.None()
.Microphone.None()
.Geolocation.Self()
.Payment.None()
.Usb.None();
});
// Custom headers
options.AddCustomHeader("X-API-Version", "v1.0");
options.AddCustomHeader("X-Powered-By", "Convey Framework");
// Remove server header
options.RemoveServerHeader = true;
});
// Custom security headers middleware
public class CustomSecurityHeadersMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<CustomSecurityHeadersMiddleware> _logger;
public CustomSecurityHeadersMiddleware(RequestDelegate next, ILogger<CustomSecurityHeadersMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
// Add security headers based on request
context.Response.OnStarting(() =>
{
var headers = context.Response.Headers;
// Content Security Policy for different endpoints
if (context.Request.Path.StartsWithSegments("/api"))
{
headers["Content-Security-Policy"] = "default-src 'none'";
}
else if (context.Request.Path.StartsWithSegments("/admin"))
{
headers["Content-Security-Policy"] = "default-src 'self'; script-src 'self' 'unsafe-inline'";
}
// API-specific headers
if (context.Request.Path.StartsWithSegments("/api"))
{
headers["X-Content-Type-Options"] = "nosniff";
headers["X-Frame-Options"] = "DENY";
headers["Cache-Control"] = "no-cache, no-store, must-revalidate";
headers["Pragma"] = "no-cache";
headers["Expires"] = "0";
}
// Remove sensitive headers
headers.Remove("Server");
headers.Remove("X-Powered-By");
headers.Remove("X-AspNet-Version");
return Task.CompletedTask;
});
await _next(context);
}
}
// Register middleware
app.UseMiddleware<CustomSecurityHeadersMiddleware>();
app.UseSecurityHeaders();Request validation and sanitization:
// Input validation middleware
public class InputValidationMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<InputValidationMiddleware> _logger;
public InputValidationMiddleware(RequestDelegate next, ILogger<InputValidationMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
// Validate request size
if (context.Request.ContentLength > 10 * 1024 * 1024) // 10MB limit
{
context.Response.StatusCode = 413;
await context.Response.WriteAsync("Request too large");
return;
}
// Validate content type for POST/PUT requests
if (context.Request.Method is "POST" or "PUT")
{
var contentType = context.Request.ContentType;
if (!IsAllowedContentType(contentType))
{
context.Response.StatusCode = 415;
await context.Response.WriteAsync("Unsupported media type");
return;
}
}
// Validate headers
foreach (var header in context.Request.Headers)
{
if (ContainsSuspiciousContent(header.Value))
{
_logger.LogWarning("Suspicious header detected: {HeaderName}", header.Key);
context.Response.StatusCode = 400;
await context.Response.WriteAsync("Invalid request");
return;
}
}
// Validate query parameters
foreach (var param in context.Request.Query)
{
if (ContainsSuspiciousContent(param.Value))
{
_logger.LogWarning("Suspicious query parameter detected: {ParamName}", param.Key);
context.Response.StatusCode = 400;
await context.Response.WriteAsync("Invalid request");
return;
}
}
await _next(context);
}
private bool IsAllowedContentType(string contentType)
{
var allowedTypes = new[]
{
"application/json",
"application/xml",
"text/plain",
"multipart/form-data",
"application/x-www-form-urlencoded"
};
return allowedTypes.Any(type => contentType?.StartsWith(type) == true);
}
private bool ContainsSuspiciousContent(string content)
{
var suspiciousPatterns = new[]
{
@"<script",
@"javascript:",
@"vbscript:",
@"onload=",
@"onerror=",
@"eval\(",
@"document\.cookie",
@"window\.location"
};
return suspiciousPatterns.Any(pattern =>
Regex.IsMatch(content, pattern, RegexOptions.IgnoreCase));
}
}
// Request sanitization
public static class RequestSanitizer
{
public static string SanitizeInput(string input)
{
if (string.IsNullOrEmpty(input))
return input;
// Remove potentially dangerous characters
input = input.Replace("<", "<")
.Replace(">", ">")
.Replace("\"", """)
.Replace("'", "'")
.Replace("/", "/");
// Remove script tags
input = Regex.Replace(input, @"<script[^>]*>.*?</script>", "", RegexOptions.IgnoreCase);
// Remove event handlers
input = Regex.Replace(input, @"on\w+\s*=", "", RegexOptions.IgnoreCase);
return input;
}
public static T SanitizeObject<T>(T obj) where T : class
{
if (obj == null) return obj;
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.CanRead && p.CanWrite && p.PropertyType == typeof(string));
foreach (var property in properties)
{
var value = property.GetValue(obj) as string;
if (!string.IsNullOrEmpty(value))
{
property.SetValue(obj, SanitizeInput(value));
}
}
return obj;
}
}
// Usage in endpoints
app.UseEndpoints(endpoints =>
{
endpoints.Post<CreateUserCommand>("/api/users", async (command, ctx) =>
{
// Sanitize input
RequestSanitizer.SanitizeObject(command);
var commandDispatcher = ctx.RequestServices.GetRequiredService<ICommandDispatcher>();
await commandDispatcher.SendAsync(command);
ctx.Response.StatusCode = 201;
});
});
app.UseMiddleware<InputValidationMiddleware>();public class WebApiSecurityOptions
{
public bool EnableCors { get; set; } = true;
public bool EnableRateLimiting { get; set; } = true;
public bool EnableSecurityHeaders { get; set; } = true;
public bool EnableInputValidation { get; set; } = true;
public bool EnableAuditLogging { get; set; } = true;
public string[] AllowedContentTypes { get; set; } = new[] { "application/json" };
public long MaxRequestSize { get; set; } = 10 * 1024 * 1024; // 10MB
}public static class ConveyExtensions
{
public static IConveyBuilder AddWebApiSecurity(this IConveyBuilder builder, Action<WebApiSecurityOptions> configure = null);
public static IConveyBuilder AddSecurityHeaders(this IConveyBuilder builder, Action<SecurityHeadersOptions> configure = null);
public static IApplicationBuilder UseSecurityHeaders(this IApplicationBuilder app);
public static IApplicationBuilder UseInputValidation(this IApplicationBuilder app);
}- Defense in depth - Implement multiple layers of security
- Principle of least privilege - Grant minimum required permissions
- Input validation - Validate and sanitize all inputs
- Secure headers - Implement comprehensive security headers
- Rate limiting - Protect against abuse and DoS attacks
- Audit logging - Log security-relevant events
- Regular updates - Keep security packages updated
- Security testing - Regularly test security implementations
-
CORS errors
- Check origin configuration in CORS policy
- Verify preflight request handling
- Ensure credentials are properly configured
-
Rate limiting not working
- Verify rate limiter is registered and configured
- Check partition key generation
- Ensure middleware order is correct
-
Authentication failures
- Check JWT token validation settings
- Verify certificate configuration
- Ensure proper claim mapping
-
Authorization denials
- Verify policy requirements
- Check user roles and claims
- Ensure handlers are registered