From c887d6d9d8ce1e39a7af4c96e8dbd2e970c1917b Mon Sep 17 00:00:00 2001 From: Adam Moussa Date: Fri, 5 Jun 2026 11:56:54 -0400 Subject: [PATCH] fix(security): remove hardcoded secrets from source Replace all hardcoded credentials with configuration-injected values: - SQL Server connection strings -> ${CONNECTION_STRING} env-var placeholders (4 appsettings files) - SendGrid API keys -> ${SENDGRID_API_KEY} (incl. commented copies in SendMessage.cs) - JWT signing secret -> ${JWT_SECRET} (3 appsettings files) - AWS access key pair in UploadFileHp.cs -> DI-injected IAmazonS3 (SDK default credential chain) - Google Maps API keys in App.razor / Home.razor -> IConfiguration lookup - Legacy SMTP credentials in SendMessage.cs comments -> placeholders Add .env.example documenting required environment variables and a Configuration & Secrets section in BACKEND_ARCHITECTURE.md. All exposed credentials were rotated 2026-06-05 prior to this scrub. Source: github-audit-report.md Criticals 1-2 (Agent A4). Verified: dotnet build 0 errors; secret-pattern grep clean. --- .env.example | 34 +++++++++++++++++++ Api.SeaHavenIndustries/Helper/SendMessage.cs | 10 +++--- .../appsettings.Development.json | 13 +++---- .../appsettings.Production.json | 10 ++++-- Api.SeaHavenIndustries/appsettings.json | 13 +++---- BACKEND_ARCHITECTURE.md | 33 ++++++++++++++++++ SeaHavenIndustries/Components/App.razor | 5 +-- .../Components/Pages/Home.razor | 5 +-- SeaHavenIndustries/Helper/SendMessage.cs | 10 +++--- SeaHavenIndustries/Helper/UploadFileHp.cs | 11 +++--- SeaHavenIndustries/appsettings.json | 14 +++++--- 11 files changed, 118 insertions(+), 40 deletions(-) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..cb07e57 --- /dev/null +++ b/.env.example @@ -0,0 +1,34 @@ +# Sea Haven Industries — shoc-backend required configuration +# +# This file documents the secrets that used to be hardcoded in appsettings*.json +# and source files. Copy the values into one of the supported configuration sources; +# do NOT commit real values. +# +# .NET resolves configuration in this order (later wins): +# 1. appsettings.json / appsettings.{Environment}.json (committed — placeholders only) +# 2. User Secrets (local dev): dotnet user-secrets set "Key:Sub" "value" +# 3. Environment variables (use "__" as the section separator) +# +# Environment-variable form is shown below. In AWS Elastic Beanstalk these map to +# environment properties; locally you can export them or use dotnet user-secrets. +# +# AWS credentials for the S3 client are NOT listed here on purpose: UploadFileHp now +# uses the AWS SDK default credential chain (env AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY, +# the shared profile/SSO, or the EC2/ECS instance role). Prefer an instance role in prod. + +# --- SQL Server connection string (Api.SeaHavenIndustries + SeaHavenIndustries) --- +ConnectionStrings__DefaultConnection=Server=;Initial Catalog=;User Id=;Password=;MultipleActiveResultSets=true + +# --- SendGrid (transactional email) --- +SendGrid__ApiKey=SG.xxxxxxxxxxxxxxxxxxxxxx + +# --- JWT signing secret (Api.SeaHavenIndustries) --- +JWT__Secret= + +# --- Google Maps / Places API key (SeaHavenIndustries Blazor app) --- +GoogleMaps__ApiKey=AIzaSyXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + +# --- AWS S3 (optional; prefer instance role / SSO over static keys) --- +# AWS_ACCESS_KEY_ID= +# AWS_SECRET_ACCESS_KEY= +# AWS_REGION=us-east-2 diff --git a/Api.SeaHavenIndustries/Helper/SendMessage.cs b/Api.SeaHavenIndustries/Helper/SendMessage.cs index 23fb80d..fc5bf0d 100644 --- a/Api.SeaHavenIndustries/Helper/SendMessage.cs +++ b/Api.SeaHavenIndustries/Helper/SendMessage.cs @@ -27,19 +27,19 @@ namespace Api.SeaHavenIndustries.Helper //mail.Body = body; //mail.IsBodyHtml = true; //SmtpClient smtp = new SmtpClient("mail.codevoir.com"); - //NetworkCredential Credentials = new NetworkCredential("infoesquall@codevoir.com", "Abc123!@#"); + //NetworkCredential Credentials = new NetworkCredential("", ""); //smtp.UseDefaultCredentials = false; //smtp.Credentials = Credentials; //smtp.Port = 8889; //25 alternative port number is 8889 //smtp.EnableSsl = false; //smtp.Send(mail); - //var apiKey = Environment.GetEnvironmentVariable("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\r\n"); - //var apiKey1 = Environment.GetEnvironmentVariable("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"); + //var apiKey = Environment.GetEnvironmentVariable("\r\n"); + //var apiKey1 = Environment.GetEnvironmentVariable(""); // var apiKey2 = Environment.GetEnvironmentVariable("SENDGRID_API_KEY"); var apiKey = _configuration.GetValue("SendGrid:ApiKey"); - //var apiKey = "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"; + //var apiKey = ""; var client = new SendGridClient(apiKey); var from = new EmailAddress("tech@seahavenind.com", "Sea haven Industries"); //var subject = "Sending with SendGrid is Fun"; @@ -65,7 +65,7 @@ namespace Api.SeaHavenIndustries.Helper var apiKey = _configuration.GetValue("SendGrid:ApiKey"); - //var apiKey = "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"; + //var apiKey = ""; var client = new SendGridClient(apiKey); var from = new EmailAddress("tech@seahavenind.com", "Sea haven Industries"); //var subject = "Sending with SendGrid is Fun"; diff --git a/Api.SeaHavenIndustries/appsettings.Development.json b/Api.SeaHavenIndustries/appsettings.Development.json index d2f99f1..93b5c5f 100644 --- a/Api.SeaHavenIndustries/appsettings.Development.json +++ b/Api.SeaHavenIndustries/appsettings.Development.json @@ -2,11 +2,10 @@ "ConnectionStrings": { //"DefaultConnection": "Server=DESKTOP-64LC14O\SQLEXPRESS;Database=SeahavenIndustries;TrustServerCertificate=True;Trusted_Connection=True;MultipleActiveResultSets=true", - // Test database - //"DefaultConnection": "Server=SQL5112.site4now.net;Initial Catalog=db_a7141e_seahavenindustries;User Id=db_a7141e_seahavenindustries_admin;Password=Abc123!@#;MultipleActiveResultSets=true" - //Client Database - "DefaultConnection": "Server=SQL5112.site4now.net;Initial Catalog=db_a7141e_seahavenindustry;User Id=db_a7141e_seahavenindustry_admin;Password=Abc123!@#;MultipleActiveResultSets=true" + // Provide the real value via environment variable ConnectionStrings__DefaultConnection or user-secrets. + // Do NOT commit credentials. This file is committed, so keep only the placeholder here. + "DefaultConnection": "${CONNECTION_STRING}" }, "Logging": { "LogLevel": { @@ -15,12 +14,14 @@ } }, "SendGrid": { - "ApiKey": "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" + // Provide the real value via environment variable SendGrid__ApiKey or user-secrets. + "ApiKey": "${SENDGRID_API_KEY}" }, "AllowedHosts": "*", "JWT": { "ValidAudience": "http://localhost:4200", "ValidIssuer": "http://localhost:7195", - "Secret": "JWTAuthenticationHIGHsecuredPasswordVVVp1OH7Xzyr" + // Provide the real value via environment variable JWT__Secret or user-secrets. + "Secret": "${JWT_SECRET}" } } \ No newline at end of file diff --git a/Api.SeaHavenIndustries/appsettings.Production.json b/Api.SeaHavenIndustries/appsettings.Production.json index d83ffb1..651d68c 100644 --- a/Api.SeaHavenIndustries/appsettings.Production.json +++ b/Api.SeaHavenIndustries/appsettings.Production.json @@ -1,6 +1,8 @@ { "ConnectionStrings": { - "DefaultConnection": "Server=SQL5112.site4now.net;Initial Catalog=db_a7141e_seahavenindustry;User Id=db_a7141e_seahavenindustry_admin;Password=Abc123!@#;MultipleActiveResultSets=true" + // Provide the real value via environment variable ConnectionStrings__DefaultConnection + // (e.g. Elastic Beanstalk environment property) or the instance secret store. Do NOT commit credentials. + "DefaultConnection": "${CONNECTION_STRING}" }, "Logging": { "LogLevel": { @@ -9,12 +11,14 @@ } }, "SendGrid": { - "ApiKey": "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" + // Provide the real value via environment variable SendGrid__ApiKey or the instance secret store. + "ApiKey": "${SENDGRID_API_KEY}" }, "AllowedHosts": "*", "JWT": { "ValidAudience": "http://console.seahavenind.com", "ValidIssuer": "http://console.seahavenind.com", - "Secret": "JWTAuthenticationHIGHsecuredPasswordVVVp1OH7Xzyr" + // Provide the real value via environment variable JWT__Secret or the instance secret store. + "Secret": "${JWT_SECRET}" } } diff --git a/Api.SeaHavenIndustries/appsettings.json b/Api.SeaHavenIndustries/appsettings.json index d2f99f1..2d5dc80 100644 --- a/Api.SeaHavenIndustries/appsettings.json +++ b/Api.SeaHavenIndustries/appsettings.json @@ -2,11 +2,10 @@ "ConnectionStrings": { //"DefaultConnection": "Server=DESKTOP-64LC14O\SQLEXPRESS;Database=SeahavenIndustries;TrustServerCertificate=True;Trusted_Connection=True;MultipleActiveResultSets=true", - // Test database - //"DefaultConnection": "Server=SQL5112.site4now.net;Initial Catalog=db_a7141e_seahavenindustries;User Id=db_a7141e_seahavenindustries_admin;Password=Abc123!@#;MultipleActiveResultSets=true" - //Client Database - "DefaultConnection": "Server=SQL5112.site4now.net;Initial Catalog=db_a7141e_seahavenindustry;User Id=db_a7141e_seahavenindustry_admin;Password=Abc123!@#;MultipleActiveResultSets=true" + // Provide the real value via environment variable ConnectionStrings__DefaultConnection, + // user-secrets, or appsettings.Development.json (which is gitignored). Do NOT commit credentials. + "DefaultConnection": "${CONNECTION_STRING}" }, "Logging": { "LogLevel": { @@ -15,12 +14,14 @@ } }, "SendGrid": { - "ApiKey": "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" + // Provide the real value via environment variable SendGrid__ApiKey or user-secrets. + "ApiKey": "${SENDGRID_API_KEY}" }, "AllowedHosts": "*", "JWT": { "ValidAudience": "http://localhost:4200", "ValidIssuer": "http://localhost:7195", - "Secret": "JWTAuthenticationHIGHsecuredPasswordVVVp1OH7Xzyr" + // Provide the real value via environment variable JWT__Secret or user-secrets. + "Secret": "${JWT_SECRET}" } } \ No newline at end of file diff --git a/BACKEND_ARCHITECTURE.md b/BACKEND_ARCHITECTURE.md index 9787f11..ee1d809 100644 --- a/BACKEND_ARCHITECTURE.md +++ b/BACKEND_ARCHITECTURE.md @@ -529,3 +529,36 @@ See these files for complete examples: - **Asset**: `AssetController.cs`, `AssetService.cs`, `AssetDataService.cs` All follow the same Clean Architecture pattern! + +--- + +## 🔐 Configuration & Secrets + +No secrets are stored in source. The committed `appsettings*.json` files hold only +`${PLACEHOLDER}` tokens; real values must be supplied at runtime through one of the +standard .NET configuration sources (later sources win): + +1. `appsettings.json` / `appsettings.{Environment}.json` — committed, placeholders only. +2. **User Secrets** (local dev): `dotnet user-secrets set "Section:Key" "value"`. +3. **Environment variables** — use `__` (double underscore) as the section separator. + In AWS Elastic Beanstalk these are the environment properties. + +See `.env.example` in the repo root for the full list. Required values: + +| Setting | Env-var form | Used by | Notes | +|---|---|---|---| +| `ConnectionStrings:DefaultConnection` | `ConnectionStrings__DefaultConnection` | Both projects | SQL Server connection string | +| `SendGrid:ApiKey` | `SendGrid__ApiKey` | Both projects | Transactional email | +| `JWT:Secret` | `JWT__Secret` | `Api.SeaHavenIndustries` | JWT signing key | +| `GoogleMaps:ApiKey` | `GoogleMaps__ApiKey` | `SeaHavenIndustries` (Blazor) | Maps/Places; read in `App.razor` + `Home.razor` | + +### AWS credentials + +`UploadFileHp` (S3 uploads) uses the **AWS SDK default credential chain** — it no longer +hardcodes access keys. Credentials resolve from `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` +env vars, the shared profile/SSO, or (preferred in production) the EC2/ECS instance role. +The region comes from `AddDefaultAWSOptions` in `Program.cs`. + +> ⚠️ The secrets previously committed to this repo (DB password, SendGrid key, JWT secret, +> Google Maps keys, AWS access key) should be considered compromised and **rotated**. + diff --git a/SeaHavenIndustries/Components/App.razor b/SeaHavenIndustries/Components/App.razor index 18879dd..9405fa3 100644 --- a/SeaHavenIndustries/Components/App.razor +++ b/SeaHavenIndustries/Components/App.razor @@ -1,4 +1,5 @@ - +@inject IConfiguration Configuration + @@ -17,7 +18,7 @@ - + diff --git a/SeaHavenIndustries/Components/Pages/Home.razor b/SeaHavenIndustries/Components/Pages/Home.razor index 2166132..18582f7 100644 --- a/SeaHavenIndustries/Components/Pages/Home.razor +++ b/SeaHavenIndustries/Components/Pages/Home.razor @@ -298,7 +298,7 @@
- { { "disableDoubleClickZoom", true } }) + { { "disableDoubleClickZoom", true } }) Zoom=@zoom Center=@(new GoogleMapPosition() { Lat = 42.6977, Lng = 23.3219 }) MapClick=@OnMapClick> @foreach (var itm in setmappins) @@ -341,6 +341,7 @@ @inject IAccountRepository _aservice; @inject ISettingsRepository _Seservice; @inject HttpClient Http +@inject IConfiguration Configuration @code { private IEnumerable? users { get; set; } @@ -508,7 +509,7 @@ } private async Task<(double Latitude, double Longitude)> GetLatLng(string placeId) { - const string apiKey = "AIzaSyBYwcFy_LS_UpdCIv7KZJUVhj9kMyPFdmk"; // Replace with your API key + var apiKey = Configuration["GoogleMaps:ApiKey"]; // Injected from configuration (env var GoogleMaps__ApiKey / user-secrets) //var apiUrl = $"https://places.googleapis.com/v1/places/{placeId}"; var apiUrl = $"https://maps.googleapis.com/maps/api/place/autocomplete/json?input=Vict&language=pt_BR&types=%28cities%29&key={apiKey}"; // var apiUrl = $"https://places.googleapis.com/v1/places/{placeId}?fields=id,displayName&key={apiKey}"; diff --git a/SeaHavenIndustries/Helper/SendMessage.cs b/SeaHavenIndustries/Helper/SendMessage.cs index ba84cc2..bc51b34 100644 --- a/SeaHavenIndustries/Helper/SendMessage.cs +++ b/SeaHavenIndustries/Helper/SendMessage.cs @@ -31,19 +31,19 @@ namespace SeaHavenIndustries.Helper //mail.Body = body; //mail.IsBodyHtml = true; //SmtpClient smtp = new SmtpClient("mail.codevoir.com"); - //NetworkCredential Credentials = new NetworkCredential("infoesquall@codevoir.com", "Abc123!@#"); + //NetworkCredential Credentials = new NetworkCredential("", ""); //smtp.UseDefaultCredentials = false; //smtp.Credentials = Credentials; //smtp.Port = 8889; //25 alternative port number is 8889 //smtp.EnableSsl = false; //smtp.Send(mail); - //var apiKey = Environment.GetEnvironmentVariable("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\r\n"); - //var apiKey1 = Environment.GetEnvironmentVariable("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"); + //var apiKey = Environment.GetEnvironmentVariable("\r\n"); + //var apiKey1 = Environment.GetEnvironmentVariable(""); // var apiKey2 = Environment.GetEnvironmentVariable("SENDGRID_API_KEY"); var apiKey = _configuration.GetValue("SendGrid:ApiKey"); - //var apiKey = "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"; + //var apiKey = ""; var client = new SendGridClient(apiKey); var from = new EmailAddress("tech@seahavenind.com", "Sea haven Industries"); //var subject = "Sending with SendGrid is Fun"; @@ -69,7 +69,7 @@ namespace SeaHavenIndustries.Helper var apiKey = _configuration.GetValue("SendGrid:ApiKey"); - //var apiKey = "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"; + //var apiKey = ""; var client = new SendGridClient(apiKey); var from = new EmailAddress("tech@seahavenind.com", "Sea haven Industries"); //var subject = "Sending with SendGrid is Fun"; diff --git a/SeaHavenIndustries/Helper/UploadFileHp.cs b/SeaHavenIndustries/Helper/UploadFileHp.cs index 3244dfe..b28cda2 100644 --- a/SeaHavenIndustries/Helper/UploadFileHp.cs +++ b/SeaHavenIndustries/Helper/UploadFileHp.cs @@ -4,7 +4,6 @@ using Microsoft.AspNetCore.Components.Forms; using SeaHavenIndustries.ViewModel; using Amazon.S3; using Amazon.S3.Model; -using Amazon.Runtime; namespace SeaHavenIndustries.Helper { @@ -16,13 +15,13 @@ namespace SeaHavenIndustries.Helper public UploadFileHp(IWebHostEnvironment hosting, IHttpContextAccessor httpContextAccessor, IAmazonS3 s3Client) { - _hosting = hosting; _httpContextAccessor = httpContextAccessor; - var awsCredentials = new BasicAWSCredentials("AKIAUY6EP2OAGEFSEP4W", "7xF+1RoO0pqyAWu/Lt9zP3z5HvHS/UjcCx2KbDbc"); - - _s3Client = new AmazonS3Client(awsCredentials, Amazon.RegionEndpoint.USEast2); - + // Use the IAmazonS3 client supplied by DI (registered via AddAWSService()). + // It resolves credentials through the AWS SDK default credential chain + // (environment variables, shared profile/SSO, or the EC2/ECS instance role) and the + // region from AddDefaultAWSOptions in Program.cs. Do NOT hardcode access keys here. + _s3Client = s3Client; } //public async Task UploadFiles(IBrowserFile file, string? path) diff --git a/SeaHavenIndustries/appsettings.json b/SeaHavenIndustries/appsettings.json index 9e739e2..78d143e 100644 --- a/SeaHavenIndustries/appsettings.json +++ b/SeaHavenIndustries/appsettings.json @@ -2,11 +2,10 @@ "ConnectionStrings": { //"DefaultConnection": "Server=.;Database=SeahavenIndustries;TrustServerCertificate=True;Trusted_Connection=True;MultipleActiveResultSets=true", //"DefaultConnection": "Server=.\\SqlExpress;Database=SeahavenIndustries;TrustServerCertificate=True;Trusted_Connection=True;MultipleActiveResultSets=true", - // Test database - //"DefaultConnection": "Server=SQL5112.site4now.net;Initial Catalog=db_a7141e_seahavenindustries;User Id=db_a7141e_seahavenindustries_admin;Password=Abc123!@#;MultipleActiveResultSets=true", - //Client Database - "DefaultConnection": "Server=SQL5112.site4now.net;Initial Catalog=db_a7141e_seahavenindustry;User Id=db_a7141e_seahavenindustry_admin;Password=Abc123!@#;MultipleActiveResultSets=true", + // Provide the real value via environment variable ConnectionStrings__DefaultConnection, + // user-secrets, or appsettings.Development.json (which is gitignored). Do NOT commit credentials. + "DefaultConnection": "${CONNECTION_STRING}", "excelconnection": "Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties='Excel 8.0;HDR=YES'" }, "Logging": { @@ -16,7 +15,12 @@ } }, "SendGrid": { - "ApiKey": "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" + // Provide the real value via environment variable SendGrid__ApiKey or user-secrets. + "ApiKey": "${SENDGRID_API_KEY}" + }, + "GoogleMaps": { + // Provide the real value via environment variable GoogleMaps__ApiKey or user-secrets. + "ApiKey": "${GOOGLE_MAPS_API_KEY}" }, "AllowedHosts": "*" }