SecuFile
Flux de données et sécurité
Flux de données, chiffrement et sécurité
Flux de donnees et securite
Book: SecuFile — Stockage sécurisé de fichiers | Chapter: Architecture
Flux de donnees et securite
Flux d'upload chiffre (E2E)
Le chiffrement de bout en bout est l'un des elements centraux de SecuFile. Voici le flux detaille d'un upload chiffre cote client :
1. Navigateur (React)
|-- Genere une cle AES-256 aleatoire (Data Encryption Key = DEK)
|-- Chiffre le fichier par blocs (chunks) avec AES-256-GCM
| |-- Chaque bloc utilise un nonce derive : baseNonce XOR chunkIndex
| |-- Taille des blocs optimale calculee selon la taille du fichier
|-- Recupere la cle publique RSA-4096 de l'organisation via l'API
|-- Chiffre la DEK avec RSA-OAEP (SHA-256)
|-- Envoie au serveur :
|-- Le fichier chiffre (binaire)
|-- La DEK chiffree (encrypted_key)
|-- Les metadonnees de chiffrement (nonce, chunk sizes, version)
|-- Le checksum SHA-256 du fichier original
2. Backend Go (API)
|-- Recoit le fichier chiffre + metadonnees
|-- Streaming direct vers S3 OVH (sans buffering en memoire)
|-- Stocke les metadonnees dans PostgreSQL :
|-- encrypted_key, key_version, encryption_metadata (JSONB)
|-- encryption_type = "client", uses_custom_key = true
|-- original_checksum pour verification
3. S3 OVH
|-- Stocke le fichier chiffre
|-- Le serveur n'a jamais acces aux donnees en clairFlux de download dechiffre
1. Navigateur (React)
|-- Requete GET /api/v1/clients/:id/files/:fileId/download
|-- Recoit le fichier chiffre + metadonnees
2. Backend Go
|-- Lit le fichier depuis S3
|-- Si uses_custom_key = true : renvoie le fichier chiffre tel quel
|-- Si uses_custom_key = false : dechiffre avec la DEK du serveur
3. Navigateur (React)
|-- Recupere la cle privee RSA de l'organisation (protegee par mot de passe)
|-- Dechiffre la DEK avec RSA-OAEP
|-- Dechiffre le fichier par blocs (DecryptChunked)
| |-- Reconstruit les nonces par bloc (baseNonce XOR chunkIndex)
| |-- Verifie l'integrite de chaque bloc (tag GCM)
|-- Verifie le checksum SHA-256 du fichier reconstruit
|-- Propose le telechargement au format originalGestion des cles RSA
Organisation creee
|
v
InitializeOrganizationKeys()
|
|-- Genere une paire RSA-4096 (crypto/rsa.GenerateKey)
|-- Exporte la cle publique en PEM (PKIX)
|-- Exporte la cle privee en PEM (PKCS8)
|-- Chiffre la cle privee via KMS (kmsProvider.WrapKey)
|-- Encode en Base64 (pour stockage TEXT PostgreSQL)
|-- Stocke dans la table organizations :
|-- public_key (PEM, en clair)
|-- encrypted_private_key (Base64 du ciphertext KMS)
|-- key_version = 1Hierarchie des roles et securite d'acces
SecuFile utilise une hierarchie stricte de roles pour controler l'acces :
| Role | Niveau | Peut creer | Acces |
|---|---|---|---|
| superadmin | 1 | msp, client_admin, client_user | Tous les clients |
| msp | 2 | client_admin, client_user | Clients assignes |
| client_admin | 3 | client_user | Son organisation |
| client_user | 4 | Aucun | Ses propres fichiers |
La verification des roles est effectuee dans le middleware AuthMiddleware et RequireRole :
// middleware/auth.go
func RequireRole(roles ...string) gin.HandlerFunc {
return func(c *gin.Context) {
claims, exists := c.Get("claims")
if !exists {
c.AbortWithStatus(401)
return
}
userRole := claims.(*services.Claims).Role
for _, r := range roles {
if string(userRole) == r {
c.Next()
return
}
}
c.AbortWithStatus(403)
}
}Middleware de securite
L'API Go applique les middlewares suivants dans l'ordre :
- RequestLoggingMiddleware : Logs structures (slog) avec request_id, duree, status
- Recovery : Recuperation des panics
- CORSMiddleware : Validation des origines (jamais
*en production) - BodyLimitMiddleware : Limite globale 1MB, 25MB pour les uploads
- ContentTypeMiddleware : CSRF protection (rejette text/plain, x-www-form-urlencoded)
- MetricsMiddleware : Metriques Prometheus (compteur + duree par route)
- AuthMiddleware : Validation JWT (sur les routes protegees)
- RateLimitByIP / RateLimitByUserID : Limitation distribuee Redis
Rate Limiting
Le rate limiting utilise un script Lua atomique sur Redis pour garantir la coherence en environnement multi-pod :
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local current = tonumber(redis.call('GET', key) or '0')
if current >= limit then
return 0
end
current = redis.call('INCR', key)
if current == 1 then
redis.call('EXPIRE', key, window)
end
return 1Les limites par defaut sont configurables via variables d'environnement :
- Auth (login, register) : 20 requetes/minute par IP
- Public (share links, plans) : 60 requetes/minute par IP
- API (routes protegees) : 120 requetes/minute par user_id