Skip to content

HIPAA Security & Compliance Implementation #24

@Sakeeb91

Description

@Sakeeb91

Priority

P0

Story Points

13

Dependencies

Depends on #5 (Authentication), #6 (Shared Libraries), #8 (Monitoring)

Summary

Implement comprehensive HIPAA security controls including audit logging for PHI access, encryption at rest/transit, data retention policies, consent management, and security incident response procedures to ensure full compliance with healthcare regulations.

Background

HIPAA (Health Insurance Portability and Accountability Act) requires specific security measures for protecting Protected Health Information (PHI). Current gaps:

  • No audit logging for PHI access
  • No encryption at rest enforcement
  • No data retention/deletion policies
  • No consent management system
  • No security incident response procedures
  • No PHI de-identification utilities

Acceptance Criteria

Administrative Safeguards:

  • Security incident response plan and procedures
  • Security awareness training materials
  • Access management policies (role-based access control)
  • Workforce security policies documented

Physical Safeguards:

  • Workstation security guidelines
  • Device and media controls documented

Technical Safeguards:

  • Audit logging for ALL PHI access (read, write, modify, delete)
  • Encryption at rest for database and file storage
  • Encryption in transit (TLS 1.3 minimum)
  • Access control implementation with unique user IDs
  • Automatic logoff after inactivity period
  • Emergency access procedures

Additional Requirements:

  • Data retention policies (minimum 6 years for HIPAA)
  • Secure data deletion procedures
  • PHI de-identification utilities
  • Patient consent management system
  • Breach notification procedures
  • Business Associate Agreement (BAA) templates

Technical Implementation

Audit Logging for PHI Access

Enhanced Audit Log Schema:

CREATE TABLE phi_access_logs (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID NOT NULL REFERENCES users(id),
    patient_id UUID REFERENCES patients(id),
    encounter_id UUID REFERENCES encounters(id),
    action VARCHAR(50) NOT NULL, -- 'read', 'write', 'update', 'delete'
    resource_type VARCHAR(100) NOT NULL,
    resource_id UUID,
    phi_fields TEXT[], -- Array of PHI fields accessed
    ip_address INET,
    user_agent TEXT,
    session_id UUID,
    timestamp TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    justification TEXT, -- Required for break-glass access
    
    -- Indexes for common queries
    CONSTRAINT valid_action CHECK (action IN ('read', 'write', 'update', 'delete'))
);

CREATE INDEX idx_phi_access_user_date ON phi_access_logs(user_id, timestamp DESC);
CREATE INDEX idx_phi_access_patient ON phi_access_logs(patient_id, timestamp DESC);
CREATE INDEX idx_phi_access_timestamp ON phi_access_logs(timestamp DESC);

Audit Logging Middleware:

import { Request, Response, NextFunction } from 'express';
import { getDatabase } from '@scribemed/database';
import { logger } from '@scribemed/logging';

export interface PHIAccessContext {
  patientId?: string;
  encounterId?: string;
  resourceType: string;
  resourceId?: string;
  action: 'read' | 'write' | 'update' | 'delete';
  phiFields?: string[];
}

export async function logPHIAccess(
  userId: string,
  context: PHIAccessContext,
  req: Request
): Promise<void> {
  const db = await getDatabase();

  try {
    await db.query(
      `INSERT INTO phi_access_logs (
        user_id, patient_id, encounter_id, action, resource_type,
        resource_id, phi_fields, ip_address, user_agent, session_id
      ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`,
      [
        userId,
        context.patientId || null,
        context.encounterId || null,
        context.action,
        context.resourceType,
        context.resourceId || null,
        context.phiFields || [],
        req.ip,
        req.get('user-agent'),
        req.user?.sessionId || null,
      ]
    );

    logger.info('PHI access logged', {
      userId,
      action: context.action,
      resourceType: context.resourceType,
      patientId: context.patientId,
    });
  } catch (error) {
    logger.error('Failed to log PHI access', { error, userId, context });
    // Don't throw - logging failure shouldn't break the request
  }
}

Encryption at Rest

PostgreSQL Transparent Data Encryption:

-- Enable pgcrypto extension
CREATE EXTENSION IF NOT EXISTS pgcrypto;

-- Encrypt sensitive columns
CREATE TABLE patients (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    ssn_encrypted BYTEA, -- Encrypted SSN
    medical_record_number_encrypted BYTEA,
    -- ... other fields
);

-- Encryption/decryption functions
CREATE OR REPLACE FUNCTION encrypt_field(plaintext TEXT, key TEXT)
RETURNS BYTEA AS $$
BEGIN
    RETURN pgp_sym_encrypt(plaintext, key);
END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION decrypt_field(ciphertext BYTEA, key TEXT)
RETURNS TEXT AS $$
BEGIN
    RETURN pgp_sym_decrypt(ciphertext, key);
END;
$$ LANGUAGE plpgsql;

Application-Level Encryption:

import crypto from 'crypto';

export class PHIEncryption {
  private algorithm = 'aes-256-gcm';
  private key: Buffer;

  constructor() {
    const encryptionKey = process.env.PHI_ENCRYPTION_KEY;
    if (!encryptionKey) {
      throw new Error('PHI_ENCRYPTION_KEY not configured');
    }
    this.key = Buffer.from(encryptionKey, 'hex');
  }

  encrypt(plaintext: string): string {
    const iv = crypto.randomBytes(16);
    const cipher = crypto.createCipheriv(this.algorithm, this.key, iv);

    let encrypted = cipher.update(plaintext, 'utf8', 'hex');
    encrypted += cipher.final('hex');

    const authTag = cipher.getAuthTag();

    // Return IV + AuthTag + Encrypted data
    return iv.toString('hex') + ':' + authTag.toString('hex') + ':' + encrypted;
  }

  decrypt(ciphertext: string): string {
    const parts = ciphertext.split(':');
    const iv = Buffer.from(parts[0], 'hex');
    const authTag = Buffer.from(parts[1], 'hex');
    const encrypted = parts[2];

    const decipher = crypto.createDecipheriv(this.algorithm, this.key, iv);
    decipher.setAuthTag(authTag);

    let decrypted = decipher.update(encrypted, 'hex', 'utf8');
    decrypted += decipher.final('utf8');

    return decrypted;
  }
}

Data Retention & Deletion

Retention Policy Implementation:

// packages/utils/src/data-retention.ts
export interface RetentionPolicy {
  resourceType: string;
  retentionPeriodYears: number;
  archiveBeforeDelete: boolean;
}

const HIPAA_RETENTION_POLICIES: RetentionPolicy[] = [
  { resourceType: 'clinical_notes', retentionPeriodYears: 6, archiveBeforeDelete: true },
  { resourceType: 'encounters', retentionPeriodYears: 6, archiveBeforeDelete: true },
  { resourceType: 'prescriptions', retentionPeriodYears: 7, archiveBeforeDelete: true },
  { resourceType: 'audit_logs', retentionPeriodYears: 6, archiveBeforeDelete: false },
];

export async function applyRetentionPolicy(policy: RetentionPolicy): Promise<void> {
  const db = await getDatabase();
  const cutoffDate = new Date();
  cutoffDate.setFullYear(cutoffDate.getFullYear() - policy.retentionPeriodYears);

  // Archive data if required
  if (policy.archiveBeforeDelete) {
    await archiveOldData(policy.resourceType, cutoffDate);
  }

  // Soft delete old records
  await db.query(
    `UPDATE ${policy.resourceType}
     SET deleted_at = CURRENT_TIMESTAMP
     WHERE created_at < $1 AND deleted_at IS NULL`,
    [cutoffDate]
  );
}

Consent Management

Consent Schema:

CREATE TABLE patient_consents (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    patient_id UUID NOT NULL REFERENCES patients(id),
    consent_type VARCHAR(100) NOT NULL,
    granted BOOLEAN NOT NULL,
    granted_at TIMESTAMP WITH TIME ZONE,
    revoked_at TIMESTAMP WITH TIME ZONE,
    expires_at TIMESTAMP WITH TIME ZONE,
    granted_by_user_id UUID REFERENCES users(id),
    purpose TEXT,
    scope TEXT,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    
    CONSTRAINT valid_consent_type CHECK (consent_type IN (
        'treatment',
        'data_sharing',
        'research',
        'marketing',
        'third_party_access'
    ))
);

Implementation Steps

  1. Phase 1: Audit Logging (Week 1-2)

    • Create PHI access log schema
    • Implement audit logging middleware
    • Add to all PHI-accessing endpoints
  2. Phase 2: Encryption (Week 2-3)

    • Configure encryption at rest
    • Implement application-level encryption
    • Enforce TLS 1.3
  3. Phase 3: Data Retention (Week 3)

    • Implement retention policies
    • Create archival procedures
    • Add secure deletion
  4. Phase 4: Consent Management (Week 4)

    • Create consent schema
    • Build consent API
    • Add consent checks to data access
  5. Phase 5: Compliance Documentation (Week 5)

    • Document all security controls
    • Create incident response plan
    • Prepare for HIPAA audit

Documentation

  • docs/compliance/hipaa-compliance-checklist.md
  • docs/security/encryption.md
  • docs/security/audit-logging.md
  • docs/security/incident-response.md
  • docs/compliance/data-retention.md

Status

Open

Related Issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    backendBackend services and APIscomplianceRegulatory and compliance requirementsepic-foundationFoundational platform workp0Critical priority (blocks other work)securitySecurity and compliance

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions