7.7 KiB
7.7 KiB
Security Implementation Guide
This document outlines the security measures implemented in Notely.
🔐 Authentication Security
Password Hashing
- Algorithm: Argon2id (memory-hard, resistant to GPU attacks)
- Configuration:
- Memory: 64 MB
- Time: 1 iteration
- Parallelism: 4 threads
- Salt: 16 random bytes (cryptographically secure)
// Generated hash format:
$argon2id$v=19$m=65536,t=1,p=4$salt_hex$hash_hex
JWT Tokens
- Algorithm: HS256 (HMAC-SHA256)
- Access Token TTL: 1 hour
- Refresh Token TTL: 7 days (HTTP-only secure cookie)
- Claims:
user_id: User's MongoDB ObjectIDemail: User's email addressusername: User's usernameiat: Issued at timestampexp: Expiration timestampiss: Issuer (verified against hardcoded value)
Brute-Force Protection
- Track failed login attempts in
login_attemptscollection - Rate limit: Max 5 failed attempts per IP per 15 minutes
- Account lockout: 15 minutes after 5 consecutive failures
- Cleanup: Expired records auto-deleted via TTL index
🛡️ Authorization Security
Role-Based Access Control (RBAC)
Space Roles:
├── Owner (all permissions)
├── Editor (create/edit/delete notes)
└── Viewer (read-only)
Space-Level Data Isolation
ALL queries include mandatory space_id filter
// Correct query pattern:
db.notes.find({ space_id: spaceID, ... })
// Never allow:
db.notes.find({ user_id: userID }) // ❌ Cross-space leak possible
Middleware Authorization Flow
1. Extract JWT token → Verify signature & expiration
2. Load user credentials → Verify user is active
3. Check space membership → Verify user_id + space_id + role
4. Execute request → With space_id context
🔑 Data Encryption
At Rest
- OAuth client secrets encrypted with AES-256-GCM
- Stored in MongoDB with encryption key in environment variables
- Decryption happens only when reading from database
plaintext, err := encryptor.Encrypt(clientSecret) // Stores encrypted blob
recovered, err := encryptor.Decrypt(plaintext) // Decrypts on retrieval
In Transit
- HTTPS/TLS required in production (enforced via Nginx)
- Secure cookies:
Secure,HttpOnly,SameSite=Laxflags - All sensitive data transmitted over encrypted channels
🚨 Input Validation
Backend Validation (MANDATORY)
Every endpoint validates:
- Type validation - JSON schema validation
- Length limits - min/max string lengths
- Format validation - email, ObjectID, URL formats
- Range validation - pagination limits
type CreateNoteRequest struct {
Title string `validate:"required,min=1,max=255"`
Content string `validate:"max=50000"`
Tags []string `validate:"max=100,dive,max=50"`
}
Frontend Validation
- Input sanitization - trim whitespace
- Format validation - regex patterns
- Debounced searches - prevent query spam
- Client-side feedback - improve UX
Output Sanitization
Markdown → HTML conversion sanitized with DOMPurify:
// XSS prevention
const dirty = marked.parse(userMarkdown);
const clean = DOMPurify.sanitize(dirty);
// Blocks: scripts, event handlers, dangerous attributes
🌐 Web Security Headers
Implemented via Nginx and Go middleware:
| Header | Value | Purpose |
|---|---|---|
Strict-Transport-Security |
max-age=31536000 |
Force HTTPS |
X-Content-Type-Options |
nosniff |
Prevent MIME sniffing |
X-Frame-Options |
DENY |
Prevent clickjacking |
X-XSS-Protection |
1; mode=block |
XSS protection (older browsers) |
Content-Security-Policy |
Restrictive policy | Prevent XSS attacks |
Referrer-Policy |
strict-origin-when-cross-origin |
Referrer control |
CSP Policy:
default-src 'self'
script-src 'self' 'unsafe-inline' (for development only)
style-src 'self' 'unsafe-inline'
img-src 'self' data: https:
font-src 'self'
connect-src 'self'
frame-ancestors 'none'
🍪 Cookie Security
Access Token (via Authorization header)
- Stored in memory (not localStorage)
- Passed via
Authorization: Bearer {token}
Refresh Token (HTTP-only cookie)
http.SetCookie(w, &http.Cookie{
Name: "refresh_token",
Value: token,
Path: "/",
MaxAge: 7 * 24 * 60 * 60, // 7 days
HttpOnly: true, // ✅ Cannot access from JavaScript
Secure: true, // ✅ HTTPS only
SameSite: http.SameSiteLaxMode, // ✅ CSRF protection
})
🔄 Rate Limiting
API Rate Limiting
- General: 50 requests / second per IP
- Login: 10 requests / second per IP
- Burst allowance: 20 additional requests
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
limit_req zone=api_limit burst=20 nodelay;
Login Attempt Tracking
- Track per email + IP combination
- Maximum 5 attempts per 15 minutes
- Exponential backoff on repeated failures
🔒 Database Security
MongoDB
- Authentication: Username/password with role-based access
- Network: Runs in secure Docker network (not exposed)
- Admin credentials: Stored in Kubernetes Secrets (not in code)
- Backups: TBD - use MongoDB Atlas or encrypted backups
Connection String
mongodb://admin:password@mongodb:27017/dbname?authSource=admin
🚨 Logging & Monitoring
Security Events Logged
- ✅ User registration attempts
- ✅ Login attempts (success/failure)
- ✅ Authorization failures
- ✅ Permission denied events
- ✅ Sensitive data access
Data NOT logged
- ❌ Passwords/hashes
- ❌ JWT tokens
- ❌ Encryption keys
- ❌ OAuth secrets
🧪 Security Testing
What to Test
- Authentication: Register, login, token refresh, logout
- Authorization: RBAC enforcement, space isolation
- Input validation: Invalid data rejection
- XSS prevention: Markdown sanitization
- CSRF protection: Token validation
- Rate limiting: Too many requests blocked
- SQL Injection: MongoDB-specific (parameterized queries safe)
Manual Testing Commands
# Test invalid input
curl -X POST http://localhost:8080/api/v1/auth/login \
-d '{"email":"not-an-email","password":""}'
# Test expired token
curl -H "Authorization: Bearer expired.token.here" \
http://localhost:8080/api/v1/spaces
# Test rate limiting
for i in {1..100}; do
curl http://localhost:8080/api/v1/auth/login &
done
🛠️ Production Checklist
- Change default JWT_SECRET (min 32 characters)
- Change default ENCRYPTION_KEY (32 bytes)
- Generate TLS certificates (Let's Encrypt recommended)
- Configure Nginx SSL/TLS
- Enable HTTPS redirect
- Set up database backups
- Configure logging & monitoring
- Implement CORS whitelist (specific domains)
- Set up rate limiting (tuned to your traffic)
- Enable database authentication
- Use Kubernetes Network Policies
- Set up Pod Security Policies
- Enable audit logging
- Configure Secrets encryption at rest
📚 References
Last Updated: March 2026 Security Level: Production-Grade