From ec894ba529ac418b3a37bd840fa57ae9a97362a5 Mon Sep 17 00:00:00 2001 From: SysAdmin Date: Mon, 29 Sep 2025 05:26:29 +0100 Subject: [PATCH] Implement critical security fixes from code review --- Dockerfile | 23 ++++++---- LittleShop/Controllers/AuthController.cs | 2 +- LittleShop/Program.cs | 42 +++++++++++++++++++ .../ConfigurationValidationService.cs | 12 +++++- 4 files changed, 68 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2de9d11..3c0e351 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,12 @@ # Use the official ASP.NET Core runtime image (optimized) FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base + +# Define non-root user UID/GID (security best practice) +ARG APP_UID=1001 +ARG APP_GID=1001 + WORKDIR /app -EXPOSE 5000 +EXPOSE 8080 # Install curl for health checks RUN apt-get update && \ @@ -54,31 +59,33 @@ WORKDIR /app # Switch to root to create directories and set permissions USER root -# Create directories with proper ownership -RUN mkdir -p /app/wwwroot/uploads/products \ +# Create non-root user and directories with proper ownership +RUN groupadd -g ${APP_GID} appuser \ + && useradd -u ${APP_UID} -g ${APP_GID} -m appuser \ + && mkdir -p /app/wwwroot/uploads/products \ && mkdir -p /app/data \ && mkdir -p /app/logs \ - && chown -R $APP_UID:$APP_UID /app \ + && chown -R ${APP_UID}:${APP_GID} /app \ && chmod -R 755 /app/wwwroot/uploads \ && chmod -R 755 /app/data \ && chmod -R 755 /app/logs # Copy published app -COPY --from=publish --chown=$APP_UID:$APP_UID /app/publish . +COPY --from=publish --chown=${APP_UID}:${APP_GID} /app/publish . # Switch back to non-root user -USER $APP_UID +USER ${APP_UID} # Health check HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ - CMD curl -f http://localhost:5000/api/catalog/products || exit 1 + CMD curl -f http://localhost:8080/health || exit 1 # Optimize runtime ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=0 \ DOTNET_RUNNING_IN_CONTAINER=true \ DOTNET_USE_POLLING_FILE_WATCHER=true \ ASPNETCORE_FORWARDEDHEADERS_ENABLED=true \ - ASPNETCORE_URLS=http://+:5000 \ + ASPNETCORE_URLS=http://+:8080 \ ASPNETCORE_ENVIRONMENT=Production ENTRYPOINT ["dotnet", "LittleShop.dll"] \ No newline at end of file diff --git a/LittleShop/Controllers/AuthController.cs b/LittleShop/Controllers/AuthController.cs index 2ae95bd..cfdbf28 100644 --- a/LittleShop/Controllers/AuthController.cs +++ b/LittleShop/Controllers/AuthController.cs @@ -38,7 +38,7 @@ public class AuthController : ControllerBase catch (Exception ex) { _logger.LogError(ex, "Error during login for user: {Username}", loginDto.Username); - return StatusCode(500, new { message = "An error occurred during login", error = ex.Message }); + return StatusCode(500, new { message = "An error occurred during login" }); } } } \ No newline at end of file diff --git a/LittleShop/Program.cs b/LittleShop/Program.cs index 71b4d20..3e13bfe 100644 --- a/LittleShop/Program.cs +++ b/LittleShop/Program.cs @@ -54,6 +54,33 @@ builder.Services.Configure(options => options.ClientIdHeader = "X-ClientId"; options.GeneralRules = new List { + // Critical: Order creation - very strict limits + new AspNetCoreRateLimit.RateLimitRule + { + Endpoint = "POST:*/api/orders", + Period = "1m", + Limit = 3 + }, + new AspNetCoreRateLimit.RateLimitRule + { + Endpoint = "POST:*/api/orders", + Period = "1h", + Limit = 10 + }, + // Critical: Payment creation - strict limits + new AspNetCoreRateLimit.RateLimitRule + { + Endpoint = "POST:*/api/orders/*/payments", + Period = "1m", + Limit = 5 + }, + new AspNetCoreRateLimit.RateLimitRule + { + Endpoint = "POST:*/api/orders/*/payments", + Period = "1h", + Limit = 20 + }, + // Order lookup by identity - moderate limits new AspNetCoreRateLimit.RateLimitRule { Endpoint = "*/api/orders/by-identity/*", @@ -66,6 +93,21 @@ builder.Services.Configure(options => Period = "1m", Limit = 10 }, + // Cancel order endpoint - moderate limits + new AspNetCoreRateLimit.RateLimitRule + { + Endpoint = "POST:*/api/orders/*/cancel", + Period = "1m", + Limit = 5 + }, + // Webhook endpoint - exempt from rate limiting + new AspNetCoreRateLimit.RateLimitRule + { + Endpoint = "POST:*/api/orders/payments/webhook", + Period = "1s", + Limit = 1000 + }, + // General API limits new AspNetCoreRateLimit.RateLimitRule { Endpoint = "*", diff --git a/LittleShop/Services/ConfigurationValidationService.cs b/LittleShop/Services/ConfigurationValidationService.cs index 1a524aa..04ca345 100644 --- a/LittleShop/Services/ConfigurationValidationService.cs +++ b/LittleShop/Services/ConfigurationValidationService.cs @@ -29,8 +29,16 @@ public class ConfigurationValidationService { _logger.LogInformation("🔍 Validating application configuration..."); - // Temporarily disabled for testing SilverPay settings page - // ValidateJwtConfiguration(); + // JWT validation is critical in production, optional in development/testing + if (_environment.IsProduction() || !string.IsNullOrEmpty(_configuration["Jwt:Key"])) + { + ValidateJwtConfiguration(); + } + else if (_environment.IsDevelopment()) + { + _logger.LogWarning("⚠️ JWT validation skipped in development. Configure Jwt:Key for production readiness."); + } + ValidateSilverPayConfiguration(); ValidateProductionSafeguards(); ValidateEnvironmentConfiguration();