Security hardening: Fix critical JWT, rate limiting, and deployment issues
This commit is contained in:
@@ -6,6 +6,7 @@ using LittleShop.Data;
|
||||
using LittleShop.Services;
|
||||
using FluentValidation;
|
||||
using Serilog;
|
||||
using AspNetCoreRateLimit;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@@ -42,8 +43,58 @@ else
|
||||
options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection")));
|
||||
}
|
||||
|
||||
// Rate Limiting - protect anonymous endpoints
|
||||
builder.Services.AddMemoryCache();
|
||||
builder.Services.Configure<AspNetCoreRateLimit.IpRateLimitOptions>(options =>
|
||||
{
|
||||
options.EnableEndpointRateLimiting = true;
|
||||
options.StackBlockedRequests = false;
|
||||
options.HttpStatusCode = 429;
|
||||
options.RealIpHeader = "X-Real-IP";
|
||||
options.ClientIdHeader = "X-ClientId";
|
||||
options.GeneralRules = new List<AspNetCoreRateLimit.RateLimitRule>
|
||||
{
|
||||
new AspNetCoreRateLimit.RateLimitRule
|
||||
{
|
||||
Endpoint = "*/api/orders/by-identity/*",
|
||||
Period = "1m",
|
||||
Limit = 10
|
||||
},
|
||||
new AspNetCoreRateLimit.RateLimitRule
|
||||
{
|
||||
Endpoint = "*/api/orders/by-customer/*",
|
||||
Period = "1m",
|
||||
Limit = 10
|
||||
},
|
||||
new AspNetCoreRateLimit.RateLimitRule
|
||||
{
|
||||
Endpoint = "*",
|
||||
Period = "1s",
|
||||
Limit = 10
|
||||
},
|
||||
new AspNetCoreRateLimit.RateLimitRule
|
||||
{
|
||||
Endpoint = "*",
|
||||
Period = "1m",
|
||||
Limit = 100
|
||||
}
|
||||
};
|
||||
});
|
||||
builder.Services.AddSingleton<AspNetCoreRateLimit.IIpPolicyStore, AspNetCoreRateLimit.MemoryCacheIpPolicyStore>();
|
||||
builder.Services.AddSingleton<AspNetCoreRateLimit.IRateLimitCounterStore, AspNetCoreRateLimit.MemoryCacheRateLimitCounterStore>();
|
||||
builder.Services.AddSingleton<AspNetCoreRateLimit.IRateLimitConfiguration, AspNetCoreRateLimit.RateLimitConfiguration>();
|
||||
builder.Services.AddSingleton<AspNetCoreRateLimit.IProcessingStrategy, AspNetCoreRateLimit.AsyncKeyLockProcessingStrategy>();
|
||||
|
||||
// Authentication - Cookie for Admin Panel, JWT for API
|
||||
var jwtKey = builder.Configuration["Jwt:Key"] ?? "ThisIsASuperSecretKeyForJWTAuthenticationThatIsDefinitelyLongerThan32Characters!";
|
||||
var jwtKey = builder.Configuration["Jwt:Key"];
|
||||
if (string.IsNullOrEmpty(jwtKey))
|
||||
{
|
||||
Log.Fatal("🚨 SECURITY: Jwt:Key configuration is missing. Application cannot start securely.");
|
||||
throw new InvalidOperationException(
|
||||
"JWT:Key must be configured in environment variables or user secrets. " +
|
||||
"Set the Jwt__Key environment variable or use: dotnet user-secrets set \"Jwt:Key\" \"<your-secure-key>\"");
|
||||
}
|
||||
|
||||
var jwtIssuer = builder.Configuration["Jwt:Issuer"] ?? "LittleShop";
|
||||
var jwtAudience = builder.Configuration["Jwt:Audience"] ?? "LittleShop";
|
||||
|
||||
@@ -251,6 +302,9 @@ if (!app.Environment.IsDevelopment())
|
||||
app.UseHsts(); // Use HSTS for production security
|
||||
}
|
||||
|
||||
// Add rate limiting middleware (after CORS, before authentication)
|
||||
app.UseIpRateLimiting();
|
||||
|
||||
app.UseStaticFiles(); // Enable serving static files
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
@@ -298,9 +352,27 @@ app.MapGet("/api/version", () =>
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var context = scope.ServiceProvider.GetRequiredService<LittleShopContext>();
|
||||
|
||||
// Ensure database is created (temporary while fixing migrations)
|
||||
context.Database.EnsureCreated();
|
||||
|
||||
// Use proper migrations in production, EnsureCreated only for development/testing
|
||||
if (app.Environment.IsProduction())
|
||||
{
|
||||
Log.Information("Production environment: Applying database migrations...");
|
||||
try
|
||||
{
|
||||
context.Database.Migrate();
|
||||
Log.Information("Database migrations applied successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Fatal(ex, "Database migration failed. Application cannot start.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Information("Development/Testing environment: Using EnsureCreated");
|
||||
context.Database.EnsureCreated();
|
||||
}
|
||||
|
||||
// Seed default admin user
|
||||
var authService = scope.ServiceProvider.GetRequiredService<IAuthService>();
|
||||
@@ -310,10 +382,14 @@ using (var scope = app.Services.CreateScope())
|
||||
var dataSeeder = scope.ServiceProvider.GetRequiredService<IDataSeederService>();
|
||||
await dataSeeder.SeedSampleDataAsync();
|
||||
|
||||
// Seed system settings - enable test currencies for development
|
||||
var systemSettings = scope.ServiceProvider.GetRequiredService<ISystemSettingsService>();
|
||||
await systemSettings.SetTestCurrencyEnabledAsync("TBTC", true);
|
||||
await systemSettings.SetTestCurrencyEnabledAsync("TLTC", true);
|
||||
// Seed system settings - enable test currencies only in development
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
Log.Information("Development environment: Enabling test currencies");
|
||||
var systemSettings = scope.ServiceProvider.GetRequiredService<ISystemSettingsService>();
|
||||
await systemSettings.SetTestCurrencyEnabledAsync("TBTC", true);
|
||||
await systemSettings.SetTestCurrencyEnabledAsync("TLTC", true);
|
||||
}
|
||||
}
|
||||
|
||||
Log.Information("LittleShop API starting up...");
|
||||
|
||||
Reference in New Issue
Block a user