# ORIGIN_SECURITY_FRAMEWORK.md ## 📋 RÉSUMÉ EXÉCUTIF Ce document dĂ©finit le framework de sĂ©curitĂ© de la plateforme Veza. Il couvre la sĂ©curitĂ© applicative, infrastructure, et conformitĂ© rĂ©glementaire (GDPR). Le framework suit les pratiques OWASP et CIS Benchmarks pour protĂ©ger les donnĂ©es utilisateurs et rĂ©sister aux menaces courantes. RĂ©visĂ© le 2026-03-04 suite Ă  un audit de sĂ©curitĂ© ayant identifiĂ© deux vulnĂ©rabilitĂ©s critiques JWT (voir section 0). ## 🎯 OBJECTIFS ### Objectif Principal Construire une plateforme sĂ©curisĂ©e par conception (Security by Design) qui protĂšge les donnĂ©es utilisateurs, rĂ©siste aux menaces courantes (OWASP Top 10), et maintient la conformitĂ© GDPR/CCPA/SOC2 en permanence. ### Objectifs Secondaires - PrĂ©venir les vulnĂ©rabilitĂ©s critiques (injection, XSS, CSRF) - Chiffrer toutes les donnĂ©es sensibles (at rest, in transit) - ImplĂ©menter une authentification robuste (MFA, JWT, OAuth) - Auditer toutes les actions critiques (audit logs immuables) - DĂ©tecter et rĂ©pondre aux incidents rapidement (SIEM, alerting) ## 📖 TABLE DES MATIÈRES 0. [Corrections critiques — Audit 2026-03-04](#corrections-critiques--audit-2026-03-04) 1. [Security Philosophy](#1-security-philosophy) 2. [Threat Model](#2-threat-model) 3. [Authentication Systems](#3-authentication-systems) 4. [Authorization & RBAC](#4-authorization--rbac) 5. [Data Protection](#5-data-protection) 6. [Network Security](#6-network-security) 7. [Application Security](#7-application-security) 8. [Infrastructure Security](#8-infrastructure-security) 9. [Compliance (GDPR, CCPA, SOC2)](#9-compliance-gdpr-ccpa-soc2) 10. [SĂ©curitĂ© et Ă©thique des donnĂ©es](#10-sĂ©curitĂ©-et-Ă©thique-des-donnĂ©es) 11. [Incident Response](#11-incident-response) 12. [Security Testing](#12-security-testing) ## 🔒 RÈGLES IMMUABLES 1. **Toutes les passwords DOIVENT ĂȘtre hashĂ©es** avec Argon2id ou bcrypt (cost ≄ 12) 2. **Toutes les communications DOIVENT ĂȘtre chiffrĂ©es** (TLS 1.3 minimum) 3. **Toutes les donnĂ©es sensibles au repos DOIVENT ĂȘtre chiffrĂ©es** (AES-256) 4. **JWT tokens DOIVENT expirer** (access: 15 min, refresh: 7 jours) 5. **MFA OBLIGATOIRE** pour admin et moderator 6. **Rate limiting OBLIGATOIRE** sur tous les endpoints publics 7. **Input validation OBLIGATOIRE** cĂŽtĂ© serveur (never trust client) 8. **Audit logs OBLIGATOIRES** pour actions sensibles (immuables) 9. **Security headers OBLIGATOIRES** (CSP, HSTS, X-Frame-Options, etc.) 10. **Secrets JAMAIS dans le code** (environment variables ou Vault) ## CORRECTIONS CRITIQUES — AUDIT 2026-03-04 Deux vulnĂ©rabilitĂ©s critiques identifiĂ©es lors de l'audit du 2026-03-04. PrioritĂ© P0 — Ă  corriger avant tout autre travail. ### VEZA-SEC-001 : Secret JWT par dĂ©faut en dur (P0) **Fichier** : `veza-common/src/config_rust.rs:234` Le secret JWT est dĂ©fini avec une valeur par dĂ©faut en dur. Si la variable d'environnement `JWT_SECRET` est absente, le service dĂ©marre avec un secret prĂ©visible, ce qui permet la forge de tokens arbitraires. **Correction requise** : - Supprimer la valeur par dĂ©faut du secret JWT dans `config_rust.rs:234` - Le service DOIT refuser de dĂ©marrer si `JWT_SECRET` n'est pas dĂ©fini - Ajouter un test d'intĂ©gration qui vĂ©rifie le refus de dĂ©marrage sans secret ```rust // AVANT (vulnĂ©rable) — config_rust.rs:234 let jwt_secret = env::var("JWT_SECRET").unwrap_or_else(|_| "default-secret".to_string()); // APRÈS (corrigĂ©) let jwt_secret = env::var("JWT_SECRET") .expect("JWT_SECRET must be set — refusing to start with no secret"); ``` ### VEZA-SEC-002 : DĂ©salignement issuer/audience JWT entre Go et Rust (P0) Les services Go et Rust utilisent des valeurs diffĂ©rentes pour les champs `iss` (issuer) et `aud` (audience) des tokens JWT. Un token Ă©mis par le backend Go peut ĂȘtre rejetĂ© par un service Rust (ou inversement), ou pire, acceptĂ© sans vĂ©rification de ces champs. **Correction requise** : - DĂ©finir une constante partagĂ©e pour `iss` et `aud` dans `veza-common` - Les deux backends DOIVENT valider `iss` et `aud` Ă  chaque vĂ©rification de token - Valeurs Ă  utiliser : `iss: "veza-api"`, `aud: "veza-platform"` ## 1. SECURITY PHILOSOPHY ### 1.1 Principes Fondamentaux **Defense in Depth** (multiple layers): ``` Layer 1: Network (Firewall, WAF, DDoS protection) Layer 2: Application (Input validation, output encoding) Layer 3: Authentication (JWT, MFA, session management) Layer 4: Authorization (RBAC, resource-level permissions) Layer 5: Data (Encryption at rest, in transit) Layer 6: Monitoring (SIEM, anomaly detection, alerting) ``` **Least Privilege Principle**: - Users get minimum permissions needed - Service accounts have scoped permissions - Database users have role-specific privileges **Fail Securely**: - Default deny (whitelist, not blacklist) - Errors don't leak sensitive info - Graceful degradation **Zero Trust Architecture**: - Never trust, always verify - Verify every request (even internal) - Assume breach mentality ### 1.2 Security by Design **Secure Defaults**: ```go // Good: Secure by default user := User{ Role: "user", // Not admin IsActive: false, // Requires activation EmailVerified: false, // Requires verification MFAEnabled: false, // User opts in } // Bad: Insecure defaults user := User{ Role: "admin", // DANGER IsActive: true, EmailVerified: true, } ``` **Minimise Attack Surface**: - Disable unused features - Remove debug endpoints in production - Restrict CORS origins - Limit exposed ports ## 2. THREAT MODEL ### 2.1 OWASP Top 10 (2021) | # | Threat | Mitigation | Status | |---|--------|------------|--------| | **A01** | Broken Access Control | RBAC, resource ownership checks | ✅ Implemented | | **A02** | Cryptographic Failures | TLS 1.3, AES-256, proper key management | ✅ Implemented | | **A03** | Injection | Parameterized queries, input validation | ✅ Implemented | | **A04** | Insecure Design | Security requirements, threat modeling | ✅ Implemented | | **A05** | Security Misconfiguration | Hardened configs, automated scanning | ✅ Implemented | | **A06** | Vulnerable Components | Dependency scanning, automated updates | ✅ Implemented | | **A07** | Authentication Failures | JWT + MFA, rate limiting, session management | ✅ Implemented | | **A08** | Software & Data Integrity | Signed packages, CI/CD security | ✅ Implemented | | **A09** | Security Logging Failures | Comprehensive audit logs, SIEM | ✅ Implemented | | **A10** | Server-Side Request Forgery | URL validation, allowlist | ✅ Implemented | ### 2.2 Threat Actors | Actor | Motivation | Capability | Targets | |-------|------------|------------|---------| | **Script Kiddies** | Fun, recognition | Low | Public endpoints, known vulnerabilities | | **Competitors** | Business advantage | Medium | API, data scraping | | **Cybercriminals** | Financial gain | High | User data, payment info, accounts | | **Nation States** | Espionage, disruption | Very High | Infrastructure, sensitive data | | **Malicious Insiders** | Revenge, financial | High | Database, internal systems | ### 2.3 Attack Scenarios **Scenario 1: Credential Stuffing Attack** - **Attack**: Automated login attempts with leaked credentials - **Mitigation**: Rate limiting (10 attempts/min), CAPTCHA after 3 failures, account lockout, MFA - **Detection**: Anomalous login patterns, multiple failed attempts **Scenario 2: SQL Injection** - **Attack**: Malicious SQL in user input - **Mitigation**: Parameterized queries (GORM, SQLx), input validation, WAF rules - **Detection**: SQLMap signatures, unusual query patterns **Scenario 3: XSS Attack** - **Attack**: Inject malicious JavaScript - **Mitigation**: Output encoding, CSP headers, sanitize HTML - **Detection**: WAF rules, CSP violation reports **Scenario 4: Data Breach** - **Attack**: Unauthorized access to database - **Mitigation**: Encryption at rest, minimal privileges, audit logs, network segmentation - **Detection**: Unusual query volumes, access from unknown IPs **Scenario 5: DDoS Attack** - **Attack**: Overwhelm servers with traffic - **Mitigation**: Cloudflare DDoS protection, rate limiting, auto-scaling - **Detection**: Traffic spikes, latency increases ## 3. AUTHENTICATION SYSTEMS ### 3.1 Password Authentication **Password Requirements**: ``` - Minimum length: 12 characters - Maximum length: 128 characters - Must contain: - At least 1 uppercase letter - At least 1 lowercase letter - At least 1 digit - At least 1 special character - Cannot contain: - Email or username - Common passwords (check against Have I Been Pwned) - Sequential characters (abc, 123) ``` **Password Hashing** (Go backend): ```go import "golang.org/x/crypto/bcrypt" // Hash password with bcrypt (cost 12) func HashPassword(password string) (string, error) { hash, err := bcrypt.GenerateFromPassword([]byte(password), 12) if err != nil { return "", err } return string(hash), nil } // Verify password func VerifyPassword(hash, password string) bool { err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) return err == nil } ``` **Password Hashing** (Rust services): ```rust use argon2::{Argon2, PasswordHasher, PasswordVerifier}; use argon2::password_hash::{SaltString, rand_core::OsRng}; // Hash password with Argon2id pub fn hash_password(password: &str) -> Result { let salt = SaltString::generate(&mut OsRng); let argon2 = Argon2::default(); let hash = argon2.hash_password(password.as_bytes(), &salt) .map_err(|e| anyhow!("Hash error: {}", e))?; Ok(hash.to_string()) } // Verify password pub fn verify_password(hash: &str, password: &str) -> bool { let parsed_hash = PasswordHash::new(hash).ok()?; Argon2::default() .verify_password(password.as_bytes(), &parsed_hash) .is_ok() } ``` **Password History**: ```sql -- Prevent password reuse (last 5 passwords) SELECT COUNT(*) FROM password_history WHERE user_id = $1 AND password_hash = $2 AND created_at > NOW() - INTERVAL '90 days' ``` ### 3.2 JWT Tokens **Token Structure**: ```json { "header": { "alg": "HS256", "typ": "JWT" }, "payload": { "sub": "550e8400-e29b-41d4-a716-446655440000", "email": "user@example.com", "role": "user", "token_version": 0, "iat": 1730556000, "exp": 1730556900 }, "signature": "..." } ``` **Migration HS256 → RS256 (Phase 3.5 — planifiĂ©e)** : L'algorithme actuel (HS256, secret symĂ©trique) est fonctionnel mais prĂ©sente des limites : - Un mĂȘme secret est partagĂ© entre tous les services qui vĂ©rifient les tokens - La rotation du secret nĂ©cessite un redĂ©ploiement synchronisĂ© - Pas de distinction entre capacitĂ© d'Ă©mission et capacitĂ© de vĂ©rification La migration vers RS256 (clĂ© asymĂ©trique) est planifiĂ©e pour la Phase 3.5 : - Seul le service d'authentification dĂ©tient la clĂ© privĂ©e (Ă©mission) - Les autres services ne reçoivent que la clĂ© publique (vĂ©rification) - Rotation facilitĂ©e via JWKS endpoint - PĂ©riode de transition : les deux algorithmes acceptĂ©s pendant 30 jours **Token Lifecycle**: - **Access Token**: 15 minutes (short-lived) - **Refresh Token**: 7 days (long-lived, stored in DB) **Token Generation** (Go): ```go import "github.com/golang-jwt/jwt/v5" type Claims struct { UserID uuid.UUID `json:"sub"` Email string `json:"email"` Role string `json:"role"` TokenVersion int `json:"token_version"` jwt.RegisteredClaims } func GenerateAccessToken(user *models.User) (string, error) { claims := Claims{ UserID: user.ID, Email: user.Email, Role: user.Role, TokenVersion: user.TokenVersion, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(15 * time.Minute)), IssuedAt: jwt.NewNumericDate(time.Now()), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte(os.Getenv("JWT_SECRET"))) } ``` **Token Invalidation**: ```go // Invalidate all tokens for user (logout all devices) func InvalidateAllTokens(userID uuid.UUID) error { return db.Model(&models.User{}). Where("id = ?", userID). Update("token_version", gorm.Expr("token_version + 1")). Error } ``` **Token Verification Middleware**: ```go func AuthMiddleware() gin.HandlerFunc { return func(c *gin.Context) { authHeader := c.GetHeader("Authorization") if !strings.HasPrefix(authHeader, "Bearer ") { c.JSON(401, ErrorResponse{Code: 1002, Message: "Invalid token"}) c.Abort() return } tokenString := strings.TrimPrefix(authHeader, "Bearer ") claims, err := VerifyToken(tokenString) if err != nil { c.JSON(401, ErrorResponse{Code: 1001, Message: "Token expired"}) c.Abort() return } // Check token version (for revocation) user := GetUser(claims.UserID) if user.TokenVersion != claims.TokenVersion { c.JSON(401, ErrorResponse{Code: 1002, Message: "Token revoked"}) c.Abort() return } c.Set("user", user) c.Next() } } ``` ### 3.3 Multi-Factor Authentication (MFA) **TOTP (Time-Based One-Time Password)**: ```go import "github.com/pquerna/otp/totp" // Generate TOTP secret func GenerateTOTPSecret(email string) (*otp.Key, error) { return totp.Generate(totp.GenerateOpts{ Issuer: "Veza", AccountName: email, }) } // Verify TOTP code func VerifyTOTPCode(secret, code string) bool { return totp.Validate(code, secret) } ``` **Backup Codes**: ```go // Generate 10 backup codes (8 characters each) func GenerateBackupCodes() []string { codes := make([]string, 10) for i := range codes { codes[i] = generateRandomString(8) } return codes } // Store hashed backup codes func StoreBackupCodes(userID uuid.UUID, codes []string) error { hashed := make([]string, len(codes)) for i, code := range codes { hash, _ := bcrypt.GenerateFromPassword([]byte(code), 12) hashed[i] = string(hash) } return db.Model(&models.TwoFactorConfig{}). Where("user_id = ?", userID). Update("backup_codes", hashed). Error } ``` **MFA Login Flow**: ``` 1. User enters email/password 2. Backend verifies credentials 3. IF MFA enabled: a. Return "mfa_required" response b. User enters TOTP code c. Backend verifies TOTP d. Return JWT tokens 4. ELSE: Return JWT tokens directly ``` ### 3.4 OAuth 2.0 (Social Login) **Supported Providers**: - Google OAuth 2.0 - GitHub OAuth - Discord OAuth - Spotify OAuth **OAuth Flow** (Authorization Code): ```go // Step 1: Redirect to provider func InitiateOAuth(c *gin.Context) { provider := c.Param("provider") // google, github, discord, spotify config := getOAuthConfig(provider) state := generateRandomString(32) // Store state in Redis (expires in 10 min) redis.Set("oauth_state:"+state, c.ClientIP(), 10*time.Minute) url := config.AuthCodeURL(state, oauth2.AccessTypeOffline) c.Redirect(302, url) } // Step 2: Handle callback func OAuthCallback(c *gin.Context) { code := c.Query("code") state := c.Query("state") // Verify state if !redis.Exists("oauth_state:" + state) { c.JSON(400, ErrorResponse{Code: 1009, Message: "Invalid state"}) return } // Exchange code for token token, err := config.Exchange(ctx, code) if err != nil { c.JSON(400, ErrorResponse{Code: 1009, Message: "OAuth error"}) return } // Get user info from provider userInfo := fetchUserInfo(token) // Find or create user user := findOrCreateUserByOAuth(userInfo) // Generate JWT accessToken := GenerateAccessToken(user) refreshToken := GenerateRefreshToken(user) c.JSON(200, LoginResponse{ AccessToken: accessToken, RefreshToken: refreshToken, User: user, }) } ``` ### 3.5 Session Management **Refresh Token Storage**: ```sql CREATE TABLE refresh_tokens ( id UUID PRIMARY KEY, user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, token_hash VARCHAR(255) NOT NULL UNIQUE, -- SHA-256 hash device_name VARCHAR(255), user_agent TEXT, ip_address INET, expires_at TIMESTAMPTZ NOT NULL, last_used_at TIMESTAMPTZ, is_revoked BOOLEAN NOT NULL DEFAULT false, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); ``` **Session Security**: - Store only hashed refresh tokens (SHA-256) - Track device, IP, user agent - Invalidate on password change - Revoke on logout - Auto-expire after 7 days of inactivity ## 4. AUTHORIZATION & RBAC ### 4.1 Role Hierarchy ``` admin (highest privileges) └── moderator (content moderation) └── premium (paid features) └── creator (content creation) └── user (base role) ``` ### 4.2 Permission Matrix | Resource | Action | user | creator | premium | moderator | admin | |----------|--------|------|---------|---------|-----------|-------| | **Track** | View | ✅ | ✅ | ✅ | ✅ | ✅ | | **Track** | Upload | ❌ | ✅ | ✅ | ✅ | ✅ | | **Track** | Edit Own | ❌ | ✅ | ✅ | ✅ | ✅ | | **Track** | Delete Own | ❌ | ✅ | ✅ | ✅ | ✅ | | **Track** | Delete Any | ❌ | ❌ | ❌ | ✅ | ✅ | | **User** | View Profile | ✅ | ✅ | ✅ | ✅ | ✅ | | **User** | Edit Own | ✅ | ✅ | ✅ | ✅ | ✅ | | **User** | Ban | ❌ | ❌ | ❌ | ✅ | ✅ | | **User** | Change Role | ❌ | ❌ | ❌ | ❌ | ✅ | | **Product** | Buy | ✅ | ✅ | ✅ | ✅ | ✅ | | **Product** | Sell | ❌ | ✅ | ✅ | ✅ | ✅ | | **Config** | View | ❌ | ❌ | ❌ | ❌ | ✅ | | **Config** | Edit | ❌ | ❌ | ❌ | ❌ | ✅ | ### 4.3 Resource-Level Authorization **Check Ownership**: ```go func RequireOwnership() gin.HandlerFunc { return func(c *gin.Context) { user := GetCurrentUser(c) resourceID := c.Param("id") resource := GetResource(resourceID) if resource.UserID != user.ID && user.Role != "admin" { c.JSON(403, ErrorResponse{Code: 1003, Message: "Forbidden"}) c.Abort() return } c.Next() } } // Usage r.PATCH("/tracks/:id", AuthMiddleware(), RequireOwnership(), handlers.UpdateTrack) ``` **Permission Policies** (Casbin): ```ini # policy.csv p, admin, *, * p, moderator, track, delete p, moderator, user, ban p, creator, track, create p, creator, track, update_own p, user, track, view ``` ### 4.4 API Key Authentication (for external integrations) **API Key Structure**: ``` Format: vz_live_1234567890abcdef1234567890abcdef Prefix: vz_live_ (production) or vz_test_ (sandbox) ``` **API Key Storage**: ```sql CREATE TABLE api_keys ( id UUID PRIMARY KEY, user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, key_prefix VARCHAR(20) NOT NULL, -- vz_live_1234 key_hash VARCHAR(255) NOT NULL UNIQUE, -- SHA-256 of full key name VARCHAR(255), scopes VARCHAR(50)[], -- ['read:tracks', 'write:playlists'] last_used_at TIMESTAMPTZ, expires_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); ``` ## 5. DATA PROTECTION ### 5.1 Encryption at Rest **Database Encryption** (PostgreSQL): ```sql -- Enable pgcrypto extension CREATE EXTENSION IF NOT EXISTS pgcrypto; -- Encrypt sensitive columns CREATE TABLE users ( id UUID PRIMARY KEY, email VARCHAR(255) NOT NULL, ssn BYTEA, -- Encrypted with pgcrypto phone_encrypted BYTEA ); -- Insert encrypted data INSERT INTO users (id, email, ssn) VALUES ( gen_random_uuid(), 'user@example.com', pgp_sym_encrypt('123-45-6789', 'encryption_key') ); -- Decrypt data SELECT id, email, pgp_sym_decrypt(ssn, 'encryption_key') AS ssn_decrypted FROM users; ``` **File Encryption** (S3/MinIO): ```go // Client-side encryption before upload func EncryptFile(data []byte, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(block) if err != nil { return nil, err } nonce := make([]byte, gcm.NonceSize()) if _, err := rand.Read(nonce); err != nil { return nil, err } ciphertext := gcm.Seal(nonce, nonce, data, nil) return ciphertext, nil } // Or server-side encryption (S3) s3Client.PutObject(&s3.PutObjectInput{ Bucket: aws.String("veza-files"), Key: aws.String("file.txt"), Body: bytes.NewReader(data), ServerSideEncryption: aws.String("AES256"), }) ``` **Encryption Key Management**: ``` Production: HashiCorp Vault Development: Environment variables Rotation: Every 90 days Algorithm: AES-256-GCM Key derivation: PBKDF2 or Argon2 ``` ### 5.2 Encryption in Transit **TLS Configuration** (Nginx): ```nginx server { listen 443 ssl http2; server_name api.veza.io; # TLS 1.3 only ssl_protocols TLSv1.3; # Modern cipher suite ssl_ciphers 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256'; ssl_prefer_server_ciphers off; # Certificates (Let's Encrypt) ssl_certificate /etc/letsencrypt/live/api.veza.io/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/api.veza.io/privkey.pem; # OCSP stapling ssl_stapling on; ssl_stapling_verify on; # HSTS header add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; } ``` **Database TLS** (PostgreSQL): ```go // Require SSL for database connections db, err := gorm.Open(postgres.Open( "host=db.veza.io user=veza password=xxx dbname=veza_db sslmode=require", ), &gorm.Config{}) ``` **Redis TLS**: ```go redisClient := redis.NewClient(&redis.Options{ Addr: "redis.veza.io:6380", Password: os.Getenv("REDIS_PASSWORD"), TLSConfig: &tls.Config{ MinVersion: tls.VersionTLS13, }, }) ``` ### 5.3 Data Masking & Anonymization **PII Masking** (logs): ```go func MaskEmail(email string) string { parts := strings.Split(email, "@") if len(parts) != 2 { return "***" } username := parts[0] if len(username) > 3 { username = username[:3] + "***" } else { username = "***" } return username + "@" + parts[1] } // Log: user@example.com → use***@example.com logger.Info("User logged in", zap.String("email", MaskEmail(user.Email))) ``` **Data Anonymization** (for analytics): ```sql -- Anonymize user data for analytics CREATE MATERIALIZED VIEW analytics_users AS SELECT MD5(id::text)::uuid AS anonymous_id, -- One-way hash DATE_TRUNC('month', created_at) AS signup_month, role, country -- Aggregated, not precise location FROM users; ``` ### 5.4 Data Retention & Deletion **Soft Delete** (default): ```sql -- Soft delete (set deleted_at timestamp) UPDATE users SET deleted_at = NOW() WHERE id = $1; -- Queries exclude soft-deleted SELECT * FROM users WHERE deleted_at IS NULL; ``` **Hard Delete** (GDPR right to be forgotten): ```go func DeleteUserData(userID uuid.UUID) error { // 1. Delete user data db.Unscoped().Where("id = ?", userID).Delete(&models.User{}) // 2. Anonymize related data (preserve analytics) db.Model(&models.Track{}). Where("creator_id = ?", userID). Update("creator_id", uuid.Nil) // Null user reference // 3. Delete personal files (S3) deleteUserFiles(userID) // 4. Log deletion (audit) auditLog("user_deleted", userID) return nil } ``` **Retention Policy**: ``` DonnĂ©es de compte : jusqu'Ă  demande de suppression par l'utilisateur Logs applicatifs : 90 jours (anonymisĂ©s aprĂšs expiration) Sessions : 30 jours Analytics : 12 mois (agrĂ©gĂ©es, anonymisĂ©es) Backups : 30 jours Audit logs : 24 mois ``` ## 6. NETWORK SECURITY ### 6.1 Firewall Rules **VPC Security Groups** (AWS): ```hcl # Allow HTTPS from anywhere resource "aws_security_group_rule" "allow_https" { type = "ingress" from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.web.id } # Allow PostgreSQL only from backend resource "aws_security_group_rule" "allow_postgres" { type = "ingress" from_port = 5432 to_port = 5432 protocol = "tcp" source_security_group_id = aws_security_group.backend.id security_group_id = aws_security_group.database.id } # Deny all other inbound traffic (implicit) ``` ### 6.2 WAF (Web Application Firewall) **Cloudflare WAF Rules**: ``` Rule 1: Block known bad IPs (threat intelligence) Rule 2: Rate limit login attempts (10/min per IP) Rule 3: Block SQL injection patterns (UNION SELECT, etc.) Rule 4: Block XSS patterns (