Medical Data Security: An Imperative
Health data is ultra-sensitive. A leak can have dramatic consequences:
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
Results
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).*