Security patterns and hardening practices for this monorepo. Use when implementing authentication, configuring CORS, validating input, handling secrets, securing API endpoints, or reviewing code for vulnerabilities. Triggers on tasks involving security, auth guards, env validation, CORS, CSP, rate limiting, or OWASP compliance.
Security patterns and hardening practices for this monorepo. Use when implementing authentication, configuring CORS, validating input, handling secrets, securing API endpoints, or reviewing code for vulnerabilities. Triggers on tasks involving security, auth guards, env validation, CORS, CSP, rate limiting, or OWASP compliance.
Define validation in Form Requests, not in controllers
Always set max:255 for string fields; use min:1 for required strings
Use exists:table,column rules for foreign key integrity
Use email rule for email fields
Never trust raw $request->all() — always use $request->validated()
Validation Error Response Format
Laravel returns 422 on validation failure with this shape:
{"message":"The title field is required.","errors":{"title":["The title field is required."]}}
Error Masking
Laravel's exception handler ensures internal errors never leak to clients:
// app/Exceptions/Handler.php — APP_DEBUG=false hides stack traces in production// 404s return JSON via willTerminate or shouldReturnJson()// 500s return a generic JSON error message
Rules:
Never expose stack traces in production (APP_DEBUG=false)
Log full error details via Laravel's logging system (server-side only)
Return safe messages client-side with appropriate HTTP status codes
Use abort(403) / abort(404) rather than throwing generic exceptions
Network Security (AWS)
Security Group Rules
ALB SG:
Inbound: 80 (HTTP), 443 (HTTPS) from 0.0.0.0/0
Outbound: All
EC2 ASG SG:
Inbound: 3001, 8000 from ALB SG only
Outbound: All
RDS / MySQL SG:
Inbound: 3306 from EC2 ASG SG only
Outbound: None
Rules:
EC2 instances should only be reachable via the ALB (SG-to-SG reference)
Database only accepts connections from EC2 instances (not public)
Outbound allowed for external API calls and package installs
Secret Management
Environment Variables by Risk Level
Risk
Where
Example
Public
GitHub Variables
NEXT_PUBLIC_APP_URL, AWS_REGION, PROJECT_NAME
Secret
GitHub Secrets
APP_KEY, DATABASE_URL, AWS_SECRET_ACCESS_KEY
Build-time
Docker ARG
NEXT_PUBLIC_API_BASE_URL (baked into JS bundle)
Runtime
EC2 env / Docker env
APP_KEY, DB_PASSWORD, CORS_ALLOWED_ORIGINS
Rules:
Never commit secrets to .env files (only .env.example with placeholder values)
Use GitHub Secrets for anything sensitive
APP_KEY must be a stable secret — rotating it invalidates all sessions and encrypted data
Rotate secrets regularly, especially AWS_SECRET_ACCESS_KEY
Security Checklist for New Features
Input validated via Laravel Form Request
Auth required? Route protected with auth:sanctum middleware
User ID from $request->user(), not request body
CORS allows only expected origins (CORS_ALLOWED_ORIGINS)
Error responses don't leak internals (APP_DEBUG=false in production)
Secrets in GitHub Secrets, not env files or code
Database queries use Eloquent ORM (parameterized by default)
No raw SQL with string concatenation — use DB::select() with bindings if needed
File uploads validated for type and size
Rate limiting considered for public endpoints (throttle: middleware)