Security Guide¶
Comprehensive security guidelines and best practices for the Go REST API Boilerplate (GRAB).
🔐 JWT Secret Management¶
JWT secrets are critical for token security. This section covers proper generation, storage, and validation.
Security Requirements¶
All Environments¶
- Minimum Length: 32 characters
- Cryptographically Random: Generated using secure methods (not predictable)
- Never Committed: Must never be checked into version control
- Unique per Environment: Different secrets for development, staging, and production
All Environments Requirements¶
- Minimum Length: 32+ characters for all environments
- Recommended Length: 64 characters (auto-generated by
make generate-jwt-secret) - Rotation Policy: Rotate secrets periodically (recommended: every 90 days)
Production Additional Requirements¶
- SSL Required: Database must use SSL (
sslmode: require) - Strong Passwords: Database passwords required
🔑 Generating JWT Secrets¶
Automated Generation (Recommended)¶
Quick Start with Auto-Generation¶
The quick-start command automatically generates a secure 32+ character JWT secret if missing or empty.
Generate or Check JWT Secret¶
Behavior:
- If JWT_SECRET exists in .env: Displays confirmation message (doesn't regenerate)
- If JWT_SECRET is missing: Generates and automatically saves to .env
- Secret length: 64 characters (base64 encoded from 48 random bytes)
Example Output (when JWT_SECRET missing):
Example Output (when JWT_SECRET exists):
✅ JWT_SECRET already exists in .env
💡 Current value is set (not displayed for security)
To regenerate, remove the current JWT_SECRET line from .env first
Manual Generation Methods¶
Using OpenSSL (Recommended)¶
Generate a secure 32+ character secret:
Using /dev/urandom¶
Using Python¶
Using Node.js¶
🔐 RBAC Security Best Practices¶
Admin Account Management¶
Principle of Least Privilege¶
- Minimize admin accounts: Only promote trusted users to admin role
- Regular audits: Review admin accounts quarterly
- Immediate revocation: Remove admin privileges when no longer needed
- Separation of duties: Use specialized roles instead of blanket admin access where possible
Secure Admin Creation¶
# Always use the CLI - never hardcode credentials
make create-admin
# Promote existing users only after verification
make promote-admin ID=<user_id>
Never: - ❌ Hardcode admin credentials in code or environment files - ❌ Use default/shared admin passwords - ❌ Create admins through API endpoints (no admin creation API exists by design) - ❌ Store admin credentials in version control
Role Assignment Security¶
Token-Based Role Validation¶
Roles are embedded in JWT tokens and validated on every request:
- Stateless validation: No database lookup required for role checks
- Token expiration: Roles cached only until token expires (15 minutes default)
- Re-authentication required: Role changes require user to login again
- Atomic operations: Role assignments use database transactions
Protection Against Role Elevation¶
// ✅ Good: Middleware enforces role requirements
adminGroup.Use(middleware.RequireAdmin())
// ❌ Bad: Manual role checks can be bypassed
if user.Email == "admin@example.com" {
// Fragile and insecure
}
Database-Level Security¶
Role Integrity¶
- Foreign keys: Ensure referential integrity between users and roles
- Composite primary keys: Prevent duplicate role assignments
- CASCADE deletes: Automatically clean up role assignments when users/roles deleted
- Seeded roles: Default roles (user, admin) inserted via migrations, not application code
Audit Logging Recommendations¶
While GRAB includes structured logging, consider adding admin action auditing:
// Log admin actions for security monitoring
log.Info().
Uint("admin_id", contextutil.GetUserID(c)).
Str("action", "delete_user").
Uint("target_user_id", targetID).
Msg("Admin action performed")
Production Deployment Checklist¶
Before deploying RBAC to production:
- All admin accounts created through secure CLI process
- Admin passwords meet strength requirements (8+ chars, mixed case, numbers, symbols)
- JWT secrets rotated and unique per environment
- Database connections use SSL (
sslmode: require) - Admin actions monitored in application logs
- Regular security audits scheduled (quarterly recommended)
- Backup admin account created and credentials stored securely
- Role assignment process documented for your team
Token Security with Roles¶
Refresh Token Rotation¶
When users receive new roles, they must obtain a new access token:
# After promoting user to admin
POST /api/v1/auth/refresh
# User's new access token will contain updated roles
Token Leakage Mitigation¶
- Short-lived access tokens: 15 minutes default (configurable)
- Refresh token rotation: New refresh token issued on each refresh
- Token family tracking: Detects refresh token reuse attacks
- Immediate invalidation: Logout revokes all tokens for user
See Authentication Guide for complete token security details.
Related Security Documentation¶
- RBAC Guide - Role management and implementation
- Authentication - JWT security and token management
- Configuration - Secure environment variable handling
📝 Environment Configuration¶
.env File Setup¶
1. Copy Template:
2. Generate Secret:
3. Add to .env:
Environment Validation¶
Check your environment configuration:
Example Output:
✅ .env file exists
✅ JWT_SECRET is set
✅ JWT_SECRET length: 64 characters
✅ DATABASE_PASSWORD is set
⚠️ Common Security Mistakes¶
❌ DON'T: Use Weak Secrets¶
# BAD - Too short (< 32 characters)
JWT_SECRET=mysecret
# BAD - Predictable pattern
JWT_SECRET=my-secret-key-that-is-only-32-char
# BAD - Not cryptographically random
JWT_SECRET=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
✅ DO: Use Strong Secrets¶
# GOOD - Cryptographically random, 64 chars (auto-generated)
JWT_SECRET=xKyLmNpQrStUvWzAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ
# GOOD - Generated with openssl (48 bytes = 64 chars base64)
JWT_SECRET=$(openssl rand -base64 48)
# GOOD - Generated with make command (auto-saved to .env)
make generate-jwt-secret
❌ DON'T: Commit Secrets to Git¶
✅ DO: Use Environment Variables¶
# config.yaml - GOOD
jwt:
# No secret field - must be set via JWT_SECRET env var ✅
access_token_ttl: "15m"
refresh_token_ttl: "168h"
# .env - GOOD (not committed to git)
JWT_SECRET=$(openssl rand -base64 96)
# .gitignore - GOOD
.env # ✅ Always ignored
🔄 Secret Rotation¶
When to Rotate Secrets¶
- Scheduled: Every 90 days (production)
- Security Incident: Immediately if compromise suspected
- Team Changes: When team members with access leave
- Compliance: As required by security policies
Rotation Process¶
1. Generate New Secret:
2. Update Environment:
3. Restart Application:
4. Verify:
Note: Existing JWT tokens will become invalid after rotation. Users will need to re-authenticate.
🏗️ Production Deployment¶
Environment Setup Checklist¶
- Generate JWT secret (32+ characters, 64 recommended)
- Set JWT_SECRET environment variable
- Enable SSL for database (
DATABASE_SSLMODE=require) - Set DATABASE_PASSWORD via environment variable
- Verify APP_ENVIRONMENT=production
- Disable debug mode (APP_DEBUG=false)
- Run
make check-envto validate configuration
Docker Deployment¶
docker-compose.prod.yml:
services:
app:
environment:
- APP_ENVIRONMENT=production
- JWT_SECRET=${JWT_SECRET} # From host environment
- DATABASE_PASSWORD=${DATABASE_PASSWORD}
- DATABASE_SSLMODE=require
Start Production:
# Set secrets in shell
export JWT_SECRET=$(openssl rand -base64 96)
export DATABASE_PASSWORD=$(openssl rand -base64 32)
# Start services
docker-compose -f docker-compose.prod.yml up -d
Kubernetes Deployment¶
Create Secret:
kubectl create secret generic app-secrets \
--from-literal=jwt-secret=$(openssl rand -base64 96) \
--from-literal=database-password=$(openssl rand -base64 32)
Deployment YAML:
apiVersion: apps/v1
kind: Deployment
metadata:
name: grab-api
spec:
template:
spec:
containers:
- name: api
env:
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: app-secrets
key: jwt-secret
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: database-password
🔍 Security Validation¶
Startup Validation¶
The application validates security configuration on startup:
Validation Checks: 1. JWT_SECRET is present 2. JWT_SECRET length ≥ 32 characters (64+ for production) 3. DATABASE_PASSWORD is set in production 4. SSL is enabled in production
Example Validation Error:
ERROR Failed to load configuration error="JWT_SECRET must be at least 32 characters (current: 16)
Generate secure secret: make generate-jwt-secret"
Runtime Security¶
Token Validation: - All protected endpoints validate JWT tokens - Expired tokens are automatically rejected - Invalid signatures are rejected - Token refresh requires valid refresh token
Rate Limiting: - Optional rate limiting per endpoint - Configurable via RATELIMIT_ENABLED, RATELIMIT_REQUESTS, RATELIMIT_WINDOW
📚 Additional Security Resources¶
OWASP Guidelines¶
12-Factor App Methodology¶
- Config - Store config in environment
Related Documentation¶
- Configuration Guide - Complete configuration reference
- Setup Guide - Initial setup and quick start
- Development Guide - Development best practices
🆘 Support¶
If you have security concerns or questions:
- Security Issues: Report via GitHub Security Advisories
- General Questions: Open an issue
- Documentation: Check full documentation