# 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) ```go // 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 ObjectID - `email`: User's email address - `username`: User's username - `iat`: Issued at timestamp - `exp`: Expiration timestamp - `iss`: Issuer (verified against hardcoded value) ### Brute-Force Protection - Track failed login attempts in `login_attempts` collection - 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** ```go // 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 ```go 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=Lax` flags - All sensitive data transmitted over encrypted channels ## ๐Ÿšจ Input Validation ### Backend Validation (MANDATORY) Every endpoint validates: 1. **Type validation** - JSON schema validation 2. **Length limits** - min/max string lengths 3. **Format validation** - email, ObjectID, URL formats 4. **Range validation** - pagination limits ```go 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: ```javascript // 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) ```go 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 ```nginx 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 1. **Authentication**: Register, login, token refresh, logout 2. **Authorization**: RBAC enforcement, space isolation 3. **Input validation**: Invalid data rejection 4. **XSS prevention**: Markdown sanitization 5. **CSRF protection**: Token validation 6. **Rate limiting**: Too many requests blocked 7. **SQL Injection**: MongoDB-specific (parameterized queries safe) ### Manual Testing Commands ```bash # 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 - [OWASP Top 10](https://owasp.org/www-project-top-ten/) - [MongoDB Security](https://docs.mongodb.com/manual/security/) - [JWT Best Practices](https://tools.ietf.org/html/rfc8949) - [Argon2 Specification](https://github.com/P-H-C/phc-winner-argon2) - [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) --- **Last Updated**: March 2026 **Security Level**: Production-Grade