Cybersecurity
Popular article

Medical Data Security: GDPR, Encryption and Compliance

Wapiki Team
December 20, 2025
10 min read
SecurityGDPREncryptionJWTCompliance

Medical Data Security: An Imperative

Health data is ultra-sensitive. A leak can have dramatic consequences:

  • Privacy violation
  • Social stigma
  • Identity theft
  • GDPR sanctions: up to **€20M** or **4% of turnover**
  • For Keneya, security is not optional.

    Data Encryption

    1. Encryption in 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. Encryption at Rest (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. Medical Records Encryption

    typescript
    // medical-record.service.ts
    async saveMedicalRecord(patientId: string, data: MedicalRecordData) {
      // Encrypt sensitive data
      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)
    
      // Decrypt
      const decrypted = this.encryptionService.decrypt(
        record.encryptedData,
        record.iv,
        record.tag
      )
    
      return JSON.parse(decrypted)
    }

    Authentication & Authorization

    1. JWT with 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('Invalid credentials')
      }
    
      // Access token (15min)
      const accessToken = this.jwtService.sign(
        { userId: user.id, role: user.role },
        { expiresIn: '15m' }
      )
    
      // Refresh token (7 days)
      const refreshToken = this.jwtService.sign(
        { userId: user.id },
        { expiresIn: '7d' }
      )
    
      // Save refresh token (hashed)
      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) {
      // Only doctors can create prescriptions
    }

    Audit Logs

    typescript
    // Record all sensitive actions
    @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
        })
      }
    }
    
    // Usage example
    async viewMedicalRecord(patientId: string, doctorId: string) {
      // Record access
      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)
    }

    GDPR Compliance

    1. Right of Access

    typescript
    @Get('my-data')
    async exportMyData(@User() user: User) {
      // Compile all user data
      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)
      }
    
      // Return as JSON
      return data
    }

    2. Right to be Forgotten

    typescript
    @Delete('delete-account')
    async deleteAccount(@User() user: User) {
      // Anonymize data (not delete completely for medical history)
      await this.userService.anonymize(user.id)
    
      // Delete non-critical data
      await this.notificationService.deleteUserNotifications(user.id)
    
      // Mark account as deleted
      await this.userService.markAsDeleted(user.id)
    }

    3. Explicit Consent

    typescript
    interface UserConsent {
      marketing: boolean
      dataSharing: boolean
      analytics: boolean
      consentDate: Date
    }
    
    // Record consent
    await this.consentService.update(user.id, {
      marketing: false,
      dataSharing: true,  // Share with doctors only
      analytics: true,
      consentDate: new Date()
    })

    Additional Security Measures

  • 🔒 **Rate limiting**: 100 requests/min per IP
  • 🛡️ **WAF**: Protection against SQL injection, XSS
  • 📱 **2FA**: Optional for doctors
  • 🔐 **Password policy**: Min 12 characters, complexity
  • 🚨 **Intrusion detection**: Fail2ban on SSH
  • Results

  • ✅ **0 data breaches** since launch
  • ✅ **GDPR compliance** validated by external audit
  • ✅ **E2E encryption** on all sensitive data
  • ✅ **100% of accesses** recorded in audit logs
  • Conclusion

    Medical data security is not limited to encryption: it's a global approach combining technology, processes and regulatory compliance.


    *Security audit for your application? [Contact us](/contact).*

    Did you like this article?

    Share it with your network!