Розробка системи зберігання медичних записів на блокчейні
Медичні дані — найбільш чутлива категорія персональних даних. Блокчейн тут вирішує реальну проблему: історія хвороби пацієнта фрагментована по різних клініках, пацієнт не має контролю над своїми даними, і передача записів між установами — бюрократичний кошмар. Децентралізована система змінює власність: пацієнт контролює доступ до своїх даних через криптографічні ключі.
Архітектурний принцип: дані off-chain, контроль on-chain
Зберігання медичних записів безпосередньо у блокчейні — помилкове рішення з кількох причин. По-перше, HIPAA, GDPR і більшість національних законів про охорону даних здоров'я вимагають можливості видалення даних — це несумісно з immutability блокчейну. По-друге, розмір даних (зображення, результати лабораторних тестів, відео) робить on-chain зберігання економічно недоцільним. Правильна архітектура:
- On-chain: посилання на дані (content-addressed hash), права доступу, audit log, записи про згоду
- Off-chain: зашифровані медичні дані у HIPAA-сумісному сховищі (S3, Azure Health Data Services) або децентралізованому сховищі (Ceramic, Filecoin з шифруванням)
Шифрування та управління ключами
Ключова ідея: дані зашифровані симетричним ключем (AES-256). Цей data encryption key (DEK) зашифрований публічним ключем пацієнта. Для надання доступу лікарю — DEK перешифровується публічним ключем лікаря (proxy re-encryption).
Медичний запис → шифрування AES-256 → зашифровані дані (в IPFS/Filecoin)
DEK → шифрування публічним ключем пацієнта → зашифрований DEK (у смарт-контракті)
Доступ лікаря:
зашифрований DEK → proxy re-encryption → зашифрований DEK для лікаря
Лікар розшифровує своїм приватним ключем → DEK → розшифровує дані
Proxy re-encryption (бібліотеки: NuCypher/Threshold Network, література: PRE схеми) дозволяє делегувати доступ без розкриття оригінального ключа. Це найкращий підхід для медичних систем: пацієнт видає гранту доступу лікарю на конкретний період і конкретні записи.
Архітектура смарт-контракту
EHR Registry (Електронні медичні записи)
contract EHRRegistry {
struct MedicalRecord {
bytes32 contentHash; // IPFS CID або hash зашифрованих даних
string storageURI; // URI для отримання даних
bytes encryptedDEK; // DEK зашифрований ключем пацієнта
uint256 timestamp;
address createdBy; // адреса медичної установи
RecordType recordType; // DIAGNOSIS, LAB_RESULT, PRESCRIPTION, IMAGING
bool active;
}
enum RecordType { DIAGNOSIS, LAB_RESULT, PRESCRIPTION, IMAGING, VACCINATION, SURGERY }
// patientId => recordId => MedicalRecord
mapping(bytes32 => mapping(bytes32 => MedicalRecord)) private records;
// patientId => recordIds
mapping(bytes32 => bytes32[]) private patientRecords;
// Права доступу: patientId => granteeAddress => AccessGrant
mapping(bytes32 => mapping(address => AccessGrant)) private accessGrants;
struct AccessGrant {
bytes encryptedDEK; // DEK перешифрований ключем grantee
uint256 expiresAt;
RecordType[] allowedTypes; // порожній масив = всі типи
bool active;
}
// Тільки авторизовані медичні провайдери можуть створювати записи
mapping(address => bool) public authorizedProviders;
event RecordAdded(bytes32 indexed patientId, bytes32 indexed recordId, RecordType recordType);
event AccessGranted(bytes32 indexed patientId, address indexed grantee, uint256 expiresAt);
event AccessRevoked(bytes32 indexed patientId, address indexed grantee);
function addRecord(
bytes32 patientId,
bytes32 recordId,
bytes32 contentHash,
string calldata storageURI,
bytes calldata encryptedDEK,
RecordType recordType
) external onlyAuthorizedProvider {
records[patientId][recordId] = MedicalRecord({
contentHash: contentHash,
storageURI: storageURI,
encryptedDEK: encryptedDEK,
timestamp: block.timestamp,
createdBy: msg.sender,
recordType: recordType,
active: true
});
patientRecords[patientId].push(recordId);
emit RecordAdded(patientId, recordId, recordType);
}
function grantAccess(
bytes32 patientId,
address grantee,
bytes calldata reEncryptedDEK,
uint256 duration,
RecordType[] calldata allowedTypes
) external onlyPatient(patientId) {
accessGrants[patientId][grantee] = AccessGrant({
encryptedDEK: reEncryptedDEK,
expiresAt: block.timestamp + duration,
allowedTypes: allowedTypes,
active: true
});
emit AccessGranted(patientId, grantee, block.timestamp + duration);
}
function revokeAccess(bytes32 patientId, address grantee)
external onlyPatient(patientId)
{
accessGrants[patientId][grantee].active = false;
emit AccessRevoked(patientId, grantee);
}
}
Audit Trail
Кожен доступ до записів логується immutably:
contract AuditTrail {
struct AuditEntry {
bytes32 patientId;
bytes32 recordId;
address accessor;
string action; // "READ", "WRITE", "GRANT", "REVOKE"
uint256 timestamp;
bytes32 transactionHash;
}
// Append-only log
AuditEntry[] public auditLog;
mapping(bytes32 => uint256[]) public patientAuditLog; // patientId => індекси
function logAccess(
bytes32 patientId,
bytes32 recordId,
string calldata action
) internal {
uint256 index = auditLog.length;
auditLog.push(AuditEntry({
patientId: patientId,
recordId: recordId,
accessor: msg.sender,
action: action,
timestamp: block.timestamp,
transactionHash: bytes32(0) // заповнюється при emit
}));
patientAuditLog[patientId].push(index);
}
}
Відповідність регуляторним вимогам
GDPR та право на видалення
Блокчейн immutable, але off-chain дані можна видалити. Паттерн: при видаленні — видаляємо дані у сховищі, DEK стає недоступним → зашифрований blob в IPFS стає беснам. On-chain залишаються тільки hash і метадані — це не персональні дані за визначенням (hash не дозволяє відновити вихідні дані).
function deactivateRecord(bytes32 patientId, bytes32 recordId)
external onlyPatient(patientId)
{
records[patientId][recordId].active = false;
// Off-chain: сервіс видаляє зашифровані дані зі сховища
// і знищує DEK
emit RecordDeactivated(patientId, recordId);
}
HL7 FHIR сумісність
Для інтеграції з існуючими медичними системами — дані зберігаються у форматі FHIR (Fast Healthcare Interoperability Resources) JSON. FHIR ресурси: Patient, Observation, DiagnosticReport, Condition, MedicationRequest.
Структура зберігання: FHIR JSON → AES-256 шифрування → IPFS → content hash on-chain. При отриманні доступу: розшифрування → парсинг FHIR JSON → перетворення у потрібний формат.
DID (Децентралізовані ідентифікатори)
Пацієнти та провайдери ідентифікуються через DID (W3C стандарт) замість сирих адрес Ethereum. Це забезпечує key rotation (зміну ключів без втрати ідентичності) та cross-system сумісність.
did:ethr:0x742d35... — DID на основі адреси Ethereum
did:web:hospital.example.com — DID на основі домену
did:key:z6Mkf... — DID на основі публічного ключа
Інтеграція з провайдерами охорони здоров'я
Для лікарень і клінік — інтеграція через FHIR API існуючих EMR (Electronic Medical Records) систем: Epic, Cerner, Meditech. Adapter сервіс: читає з EMR → конвертує у стандартний формат → шифрує → публікує on-chain.
| Компонент | Технологія |
|---|---|
| Смарт-контракти | Solidity + OpenZeppelin |
| Шифрування | AES-256-GCM + RSA або ECIES |
| Proxy re-encryption | Threshold Network / NuCypher |
| DID | did:ethr + DID Resolver |
| Сховище | IPFS + Filecoin або AWS S3 HIPAA |
| FHIR | HAPI FHIR (Java) або medplum (TypeScript) |
| Індексування | The Graph |
Сроки та вартість
| Фаза | Зміст | Сроки |
|---|---|---|
| Архітектура | DID схема, FHIR mapping, threat model | 1-2 тиж |
| Core контракти | Registry, consent, audit | 3-4 тиж |
| Шар шифрування | Управління ключами, proxy re-encryption | 2-3 тиж |
| Інтеграція сховища | IPFS/Filecoin, FHIR parser | 2-3 тиж |
| Інтеграція провайдера | FHIR API adapter | 2-4 тиж |
| Frontend | Портал пацієнта, UI провайдера | 3-4 тиж |
| Security audit | Контракти + крипто реалізація | 2-4 тиж |
Повна production-ready система: 4-6 місяців. MVP без proxy re-encryption та FHIR інтеграції: 2-3 місяці.







