Security
Overview
GigPilot AI implements multiple security layers to protect user data and prevent unauthorized access.
Security Layers
1. HTTP Security Headers (Helmet)
Automatically adds security headers to all responses:
| Header | Value |
|---|---|
Content-Security-Policy |
Restricts resource loading |
X-Content-Type-Options |
nosniff |
X-Frame-Options |
DENY |
Strict-Transport-Security |
max-age=31536000 |
X-XSS-Protection |
1; mode=block |
2. CORS Configuration
Only allows requests from configured origins:
FRONTEND_URL=http://localhost:3000,http://localhost:4321- Credentials are allowed (
credentials: true) - Allowed methods:
GET, POST, PUT, DELETE, OPTIONS - Requests with no origin (mobile apps, curl) are allowed
3. Rate Limiting
- 100 requests per minute per IP address
- Prevents brute-force and DDoS attacks
- Returns
429 Too Many Requestswhen exceeded
4. Authentication
- JWT-based authentication via Supabase Auth
- Tokens verified against Supabase before use
- Protected routes require valid
Authorization: Bearer <token>header - Session expiry: 7 days
5. Input Validation
All request bodies validated with Zod schemas:
// Example: Login validationconst loginSchema = z.object({ email: z.string().email(), password: z.string().min(6).optional(), fullName: z.string().optional(),});Invalid input returns 400 Bad Request with field-level error details.
6. Error Handling
- Stack traces hidden in production mode
- Generic error messages for 500 errors
- Detailed errors only in development
7. Log Redaction
Pino logger automatically redacts sensitive fields:
logger: { redact: { paths: [ 'req.headers.authorization', 'req.body.password', 'req.body.data' ], censor: '[REDACTED]' }}8. Row Level Security (RLS)
Supabase RLS ensures users can only access their own data:
- User-scoped Supabase clients include JWT token
- RLS policies filter queries by
user_id - Admin client (service role) bypasses RLS for background jobs
9. Cookie Security
Cookies signed with JWT_SECRET:
await fastify.register(cookie, { secret: env.JWT_SECRET });Sensitive Data Handling
Never Exposed in Responses
- Passwords
- API keys
- JWT secrets
- Database credentials
- Service role keys
- OAuth client secrets
- Refresh tokens (in logs)
Redacted in Logs
Authorizationheader- Request body
passwordfield - Request body
datafield (file uploads)
Environment Variables
All secrets stored in .env.local (gitignored). Never committed to repository.
Security Audit
Findings
| File | Line | Type | Severity | Status |
|---|---|---|---|---|
packages/auth/src/index.ts |
16 | JWT Secret Default | Medium | DEFAULT USED IN DEV |
apps/backend/src/config/env.ts |
16 | JWT Secret Default | Medium | DEFAULT USED IN DEV |
Note: Default JWT secrets (
gigpilot-secret-jwt-key-2026) are used when environment variables are not set. These are for development only and must be overridden in production.
Recommendations
- Rotate JWT secrets regularly in production
- Enable Supabase RLS policies for all tables
- Use HTTPS in production (enforced by Cloudflare)
- Monitor rate limit hits for potential attacks
- Audit API keys regularly and rotate if compromised
- Enable 2FA for Supabase dashboard access
- Review CORS origins before deploying to production
Threat Model
| Threat | Mitigation |
|---|---|
| Brute-force login | Rate limiting (100 req/min) |
| XSS | Helmet CSP headers, input validation |
| CSRF | CORS origin validation, SameSite cookies |
| SQL Injection | Supabase parameterized queries |
| Token theft | HTTPS, short-lived tokens, session expiry |
| Data leakage | RLS policies, log redaction |
| DDoS | Rate limiting, Cloudflare CDN |
| Man-in-the-middle | HTTPS enforcement |

