Patterns de Conformité PHI/PII pour les Soins de Santé
Patterns pour protéger les données des patients, des cliniciens et les données financières dans les applications de soins de santé. Applicable à HIPAA (États-Unis), DISHA (Inde), RGPD (UE) et à la protection générale des données de santé.
Quand l'utiliser
- Construire toute fonctionnalité qui touche aux dossiers des patients
- Mettre en œuvre le contrôle d'accès ou l'authentification pour les systèmes cliniques
- Concevoir des schémas de base de données pour les données de santé
- Construire des API qui renvoient des données de patients ou de cliniciens
- Mettre en œuvre des pistes d'audit ou des journaux
- Réviser le code pour les vulnérabilités d'exposition des données
- Configurer la sécurité au niveau des lignes (RLS) pour les systèmes de santé multi-locataires
Comment ça fonctionne
La protection des données de santé repose sur trois couches : la classification (ce qui est sensible), le contrôle d'accès (qui peut le voir) et la piste d'audit (qui l'a vu).
Classification des données
PHI (Protected Health Information) — toute donnée pouvant identifier un patient ET liée à sa santé : nom du patient, date de naissance, adresse, téléphone, email, numéros d'identification nationaux (SSN, Aadhaar, numéro NHS), numéros de dossier médical, diagnostics, médicaments, résultats de laboratoire, imagerie, détails de la police d'assurance et des réclamations, dossiers de rendez-vous et d'admission, ou toute combinaison de ce qui précède.
PII (données non sensibles pour les patients) dans les systèmes de santé : détails personnels des cliniciens/personnel, structures de frais et montants de rémunération des médecins, salaire des employés et coordonnées bancaires, informations de paiement des fournisseurs.
Contrôle d'accès : Sécurité au niveau des lignes
ALTER TABLE patients ENABLE ROW LEVEL SECURITY;
-- Limiter l'accès par établissement
CREATE POLICY "staff_read_own_facility"
ON patients FOR SELECT TO authenticated
USING (facility_id IN (
SELECT facility_id FROM staff_assignments
WHERE user_id = auth.uid() AND role IN ('doctor','nurse','lab_tech','admin')
));
-- Piste d'audit : insertion uniquement (inviolable)
CREATE POLICY "audit_insert_only" ON audit_log FOR INSERT
TO authenticated WITH CHECK (user_id = auth.uid());
CREATE POLICY "audit_no_modify" ON audit_log FOR UPDATE USING (false);
CREATE POLICY "audit_no_delete" ON audit_log FOR DELETE USING (false);
Piste d'audit
Chaque accès ou modification de PHI doit être journalisé :
interface AuditEntry {
timestamp: string;
user_id: string;
patient_id: string;
action: 'create' | 'read' | 'update' | 'delete' | 'print' | 'export';
resource_type: string;
resource_id: string;
changes?: { before: object; after: object };
ip_address: string;
session_id: string;
}
Vecteurs de fuite courants
Messages d'erreur : N'incluez jamais de données identifiant le patient dans les messages d'erreur renvoyés au client. Journalisez les détails côté serveur uniquement.
Sortie console : Ne journalisez jamais les objets complets des patients. Utilisez des identifiants de dossier internes opaques (UUID) — pas de numéros de dossier médical, d'identifiants nationaux ou de noms.
Paramètres d'URL : Ne mettez jamais de données identifiant le patient dans les chaînes de requête ou les segments de chemin qui pourraient apparaître dans les journaux ou l'historique du navigateur. Utilisez uniquement des UUID opaques.
Stockage navigateur : Ne stockez jamais de PHI dans localStorage ou sessionStorage. Conservez les PHI uniquement en mémoire, récupérez-les à la demande.
Clés de rôle de service : N'utilisez jamais la clé service_role dans le code côté client. Utilisez toujours la clé anonyme/publique et laissez la RLS appliquer les règles.
Journaux et surveillance : Ne journalisez jamais les dossiers complets des patients. Utilisez uniquement des identifiants de dossier opaques (pas de numéros de dossier médical). Nettoyez les traces de pile avant de les envoyer aux services de suivi des erreurs.
Marquage du schéma de base de données
Marquez les colonnes PHI/PII au niveau du schéma :
COMMENT ON COLUMN patients.name IS 'PHI: patient_name';
COMMENT ON COLUMN patients.dob IS 'PHI: date_of_birth';
COMMENT ON COLUMN patients.aadhaar IS 'PHI: national_id';
COMMENT ON COLUMN doctor_payouts.amount IS 'PII: financial';
Checklist de déploiement
Avant chaque déploiement :
- Aucune PHI dans les messages d'erreur ou les traces de pile
- Aucune PHI dans console.log/console.error
- Aucune PHI dans les paramètres d'URL
- Aucune PHI dans le stockage du navigateur
- Aucune clé service_role dans le code client
- RLS activée sur toutes les tables PHI/PII
- Piste d'audit pour toutes les modifications de données
- Temporisation de session configurée
- Authentification API sur tous les points de terminaison PHI
- Isolation des données inter-établissements vérifiée
Exemples
Exemple 1 : Gestion des erreurs sûre vs non sûre
// MAUVAIS — divulgue des PHI dans l'erreur
throw new Error(`Patient ${patient.name} not found in ${patient.facility}`);
// BON — erreur générique, détails journalisés côté serveur avec des identifiants opaques uniquement
logger.error('Patient lookup failed', { recordId: patient.id, facilityId });
throw new Error('Record not found');
Exemple 2 : Politique RLS pour l'isolation multi-établissements
-- Un médecin de l'établissement A ne peut pas voir les patients de l'établissement B
CREATE POLICY "facility_isolation"
ON patients FOR SELECT TO authenticated
USING (facility_id IN (
SELECT facility_id FROM staff_assignments WHERE user_id = auth.uid()
));
-- Test : connectez-vous en tant que médecin-établissement-a, interrogez les patients de l'établissement-b
-- Résultat attendu : 0 lignes retournées
Exemple 3 : Journalisation sûre
// MAUVAIS — journalise les données identifiables du patient
console.log('Processing patient:', patient);
// BON — journalise uniquement l'identifiant de dossier interne opaque
console.log('Processing record:', patient.id);
// Remarque : même patient.id doit être un UUID opaque, pas un numéro de dossier médical