헬스케어 PHI/PII 규정 준수 패턴
헬스케어 애플리케이션에서 환자 데이터, 임상의 데이터, 재무 데이터를 보호하기 위한 패턴입니다. HIPAA(미국), DISHA(인도), GDPR(유럽) 및 일반적인 헬스케어 데이터 보호에 적용 가능합니다.
사용 시점
- 환자 기록을 다루는 모든 기능을 구축할 때
- 임상 시스템에 대한 접근 제어 또는 인증을 구현할 때
- 헬스케어 데이터를 위한 데이터베이스 스키마를 설계할 때
- 환자 또는 임상의 데이터를 반환하는 API를 구축할 때
- 감사 추적 또는 로깅을 구현할 때
- 데이터 노출 취약점에 대한 코드를 검토할 때
- 다중 테넌트 헬스케어 시스템을 위한 행 수준 보안(RLS)을 설정할 때
작동 방식
헬스케어 데이터 보호는 분류(무엇이 민감한가), 접근 제어(누가 볼 수 있는가), 감사(누가 보았는가)라는 세 가지 계층에서 작동합니다.
데이터 분류
PHI(보호 건강 정보) — 환자를 식별할 수 있고 건강과 관련된 모든 데이터: 환자 이름, 생년월일, 주소, 전화번호, 이메일, 국가 식별 번호(SSN, Aadhaar, NHS 번호), 의료 기록 번호, 진단명, 약물, 검사 결과, 영상 정보, 보험 증권 및 청구 세부 정보, 진료 예약 및 입원 기록, 또는 위의 모든 조합.
PII(환자 비민감 데이터) in 헬스케어 시스템: 임상의/직원 개인 정보, 의사 수가 구조 및 지급 금액, 직원 급여 및 계좌 정보, 공급업체 지불 정보.
접근 제어: 행 수준 보안
ALTER TABLE patients ENABLE ROW LEVEL SECURITY;
-- 시설별로 접근 범위 지정
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')
));
-- 감사 로그: 삽입 전용(변조 방지)
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);
감사 추적
모든 PHI 접근 또는 수정은 기록되어야 합니다:
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;
}
일반적인 정보 유출 경로
오류 메시지: 클라이언트에 전달되는 오류 메시지에 환자 식별 데이터를 포함하지 마세요. 세부 정보는 서버 측에서만 기록하세요.
콘솔 출력: 전체 환자 객체를 기록하지 마세요. 불투명한 내부 레코드 ID(UUID)를 사용하세요. 의무 기록 번호, 국가 ID 또는 이름을 사용하지 마세요.
URL 매개변수: 로그나 브라우저 기록에 나타날 수 있는 쿼리 문자열이나 경로 세그먼트에 환자 식별 데이터를 넣지 마세요. 불투명한 UUID만 사용하세요.
브라우저 저장소: PHI를 localStorage나 sessionStorage에 저장하지 마세요. PHI는 메모리에서만 유지하고 필요할 때 가져오세요.
서비스 역할 키: 클라이언트 측 코드에서 service_role 키를 절대 사용하지 마세요. 항상 anon/게시 가능 키를 사용하고 RLS가 접근을 제어하도록 하세요.
로그 및 모니터링: 전체 환자 기록을 기록하지 마세요. 불투명한 레코드 ID만 사용하세요(의무 기록 번호 사용 금지). 오류 추적 서비스로 보내기 전에 스택 트레이스를 정리하세요.
데이터베이스 스키마 태깅
스키마 수준에서 PHI/PII 열을 표시하세요:
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';
배포 체크리스트
모든 배포 전:
- 오류 메시지나 스택 트레이스에 PHI 없음
- console.log/console.error에 PHI 없음
- URL 매개변수에 PHI 없음
- 브라우저 저장소에 PHI 없음
- 클라이언트 코드에 service_role 키 없음
- 모든 PHI/PII 테이블에 RLS 활성화
- 모든 데이터 수정에 대한 감사 추적
- 세션 시간 초과 구성
- 모든 PHI 엔드포인트에 API 인증
- 시설 간 데이터 격리 확인 완료
예제
예제 1: 안전한 오류 처리와 안전하지 않은 오류 처리
// 나쁨 — 오류에 PHI 노출
throw new Error(`Patient ${patient.name} not found in ${patient.facility}`);
// 좋음 — 일반 오류, 세부 정보는 서버 측에 불투명 ID로만 기록
logger.error('Patient lookup failed', { recordId: patient.id, facilityId });
throw new Error('Record not found');
예제 2: 다중 시설 격리를 위한 RLS 정책
-- 시설 A 의사는 시설 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()
));
-- 테스트: doctor-facility-a로 로그인, facility-b 환자 질의
-- 예상 결과: 0행 반환
예제 3: 안전한 로깅
// 나쁨 — 식별 가능한 환자 데이터 기록
console.log('Processing patient:', patient);
// 좋음 — 불투명한 내부 레코드 ID만 기록
console.log('Processing record:', patient.id);
// 참고: patient.id도 불투명한 UUID여야 하며, 의무 기록 번호가 아니어야 함