Cybersécurité
Article populaire

Sécurité des données médicales : RGPD, chiffrement et conformité

Équipe Wapiki
20 Décembre 2025
10 min de lecture
SécuritéRGPDChiffrementJWTConformité

Sécurité des données médicales : Un impératif

Les données de santé sont ultra-sensibles. Une fuite peut avoir des conséquences dramatiques :

  • Violation de vie privée
  • Stigmatisation sociale
  • Usurpation d'identité
  • Sanctions RGPD : jusqu'à **20M€** ou **4% du CA**
  • Pour Keneya, la sécurité n'est pas optionnelle.

    Chiffrement des données

    1. Chiffrement en transit (TLS 1.3)

    nginx
    # nginx.conf
    server {
      listen 443 ssl http2;
      server_name keneya.com;
    
      ssl_certificate /etc/letsencrypt/live/keneya.com/fullchain.pem;
      ssl_certificate_key /etc/letsencrypt/live/keneya.com/privkey.pem;
    
      ssl_protocols TLSv1.3;
      ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
      ssl_prefer_server_ciphers on;
    
      add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    }

    2. Chiffrement au repos (AES-256)

    typescript
    import crypto from 'crypto'
    
    class EncryptionService {
      private algorithm = 'aes-256-gcm'
      private key: Buffer
    
      constructor() {
        this.key = Buffer.from(process.env.ENCRYPTION_KEY, 'hex')
      }
    
      encrypt(text: string): { encrypted: string; iv: string; tag: string } {
        const iv = crypto.randomBytes(16)
        const cipher = crypto.createCipheriv(this.algorithm, this.key, iv)
    
        let encrypted = cipher.update(text, 'utf8', 'hex')
        encrypted += cipher.final('hex')
    
        const tag = cipher.getAuthTag()
    
        return {
          encrypted,
          iv: iv.toString('hex'),
          tag: tag.toString('hex')
        }
      }
    
      decrypt(encrypted: string, iv: string, tag: string): string {
        const decipher = crypto.createDecipheriv(
          this.algorithm,
          this.key,
          Buffer.from(iv, 'hex')
        )
    
        decipher.setAuthTag(Buffer.from(tag, 'hex'))
    
        let decrypted = decipher.update(encrypted, 'hex', 'utf8')
        decrypted += decipher.final('utf8')
    
        return decrypted
      }
    }

    3. Chiffrement des dossiers médicaux

    typescript
    // medical-record.service.ts
    async saveMedicalRecord(patientId: string, data: MedicalRecordData) {
      // Chiffrer les données sensibles
      const encrypted = this.encryptionService.encrypt(
        JSON.stringify({
          diagnosis: data.diagnosis,
          prescriptions: data.prescriptions,
          notes: data.notes
        })
      )
    
      await this.medicalRecordRepo.create({
        patientId,
        encryptedData: encrypted.encrypted,
        iv: encrypted.iv,
        tag: encrypted.tag,
        createdAt: new Date()
      })
    }
    
    async getMedicalRecord(patientId: string) {
      const record = await this.medicalRecordRepo.findByPatient(patientId)
    
      // Déchiffrer
      const decrypted = this.encryptionService.decrypt(
        record.encryptedData,
        record.iv,
        record.tag
      )
    
      return JSON.parse(decrypted)
    }

    Authentification & Autorisation

    1. JWT avec Refresh Tokens

    typescript
    // auth.service.ts
    async login(email: string, password: string) {
      const user = await this.userRepo.findByEmail(email)
    
      if (!await bcrypt.compare(password, user.passwordHash)) {
        throw new UnauthorizedException('Identifiants invalides')
      }
    
      // Access token (15min)
      const accessToken = this.jwtService.sign(
        { userId: user.id, role: user.role },
        { expiresIn: '15m' }
      )
    
      // Refresh token (7 jours)
      const refreshToken = this.jwtService.sign(
        { userId: user.id },
        { expiresIn: '7d' }
      )
    
      // Sauvegarder le refresh token (hashé)
      await this.tokenRepo.create({
        userId: user.id,
        tokenHash: await bcrypt.hash(refreshToken, 10),
        expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
      })
    
      return { accessToken, refreshToken }
    }

    2. RBAC (Role-Based Access Control)

    typescript
    // guards/roles.guard.ts
    @Injectable()
    export class RolesGuard implements CanActivate {
      canActivate(context: ExecutionContext): boolean {
        const requiredRoles = this.reflector.get<string[]>('roles', context.getHandler())
    
        const request = context.switchToHttp().getRequest()
        const user = request.user
    
        return requiredRoles.some(role => user.role === role)
      }
    }
    
    // consultation.controller.ts
    @UseGuards(JwtAuthGuard, RolesGuard)
    @Roles('doctor')
    @Post('prescriptions')
    async createPrescription(@Body() dto: CreatePrescriptionDto) {
      // Seuls les médecins peuvent créer des prescriptions
    }

    Audit Logs

    typescript
    // Enregistrer toutes les actions sensibles
    @Injectable()
    export class AuditService {
      async log(event: AuditEvent) {
        await this.auditRepo.create({
          userId: event.userId,
          action: event.action,
          resource: event.resource,
          resourceId: event.resourceId,
          ipAddress: event.ipAddress,
          userAgent: event.userAgent,
          timestamp: new Date(),
          metadata: event.metadata
        })
      }
    }
    
    // Exemple d'utilisation
    async viewMedicalRecord(patientId: string, doctorId: string) {
      // Enregistrer l'accès
      await this.auditService.log({
        userId: doctorId,
        action: 'VIEW_MEDICAL_RECORD',
        resource: 'medical_record',
        resourceId: patientId,
        ipAddress: req.ip,
        userAgent: req.headers['user-agent']
      })
    
      return this.medicalRecordService.get(patientId)
    }

    Conformité RGPD

    1. Droit d'accès

    typescript
    @Get('my-data')
    async exportMyData(@User() user: User) {
      // Compiler toutes les données de l'utilisateur
      const data = {
        profile: await this.userService.getProfile(user.id),
        consultations: await this.consultationService.getUserConsultations(user.id),
        medicalRecords: await this.medicalRecordService.getUserRecords(user.id),
        payments: await this.paymentService.getUserPayments(user.id)
      }
    
      // Retourner en JSON
      return data
    }

    2. Droit à l'oubli

    typescript
    @Delete('delete-account')
    async deleteAccount(@User() user: User) {
      // Anonymiser les données (pas supprimer complètement pour historique médical)
      await this.userService.anonymize(user.id)
    
      // Supprimer les données non critiques
      await this.notificationService.deleteUserNotifications(user.id)
    
      // Marquer le compte comme supprimé
      await this.userService.markAsDeleted(user.id)
    }

    3. Consentement explicite

    typescript
    interface UserConsent {
      marketing: boolean
      dataSharing: boolean
      analytics: boolean
      consentDate: Date
    }
    
    // Enregistrer le consentement
    await this.consentService.update(user.id, {
      marketing: false,
      dataSharing: true,  // Partage avec médecins uniquement
      analytics: true,
      consentDate: new Date()
    })

    Mesures de sécurité additionnelles

  • 🔒 **Rate limiting** : 100 requêtes/min par IP
  • 🛡️ **WAF** : Protection contre injections SQL, XSS
  • 📱 **2FA** : Optionnelle pour les médecins
  • 🔐 **Password policy** : Min 12 caractères, complexité
  • 🚨 **Intrusion detection** : Fail2ban sur SSH
  • Résultats

  • ✅ **0 fuite de données** depuis le lancement
  • ✅ **Conformité RGPD** validée par audit externe
  • ✅ **Chiffrement E2E** sur toutes les données sensibles
  • ✅ **100% des accès** enregistrés dans les audit logs
  • Conclusion

    La sécurité des données médicales ne se limite pas au chiffrement : c'est une approche globale combinant technique, processus et conformité réglementaire.


    *Audit de sécurité pour votre application ? [Contactez-nous](/contact).*

    Cet article vous a plu ?

    Partagez-le avec votre réseau !