Разработка системы хранения медицинских записей на блокчейне
Медицинские данные — наиболее чувствительная категория персональных данных. Блокчейн здесь решает реальную проблему: история болезни пациента фрагментирована по разным клиникам, у пациента нет контроля над собственными данными, и передача записей между учреждениями — бюрократический кошмар. Децентрализованная система меняет ownership: пациент контролирует доступ к своим данным через cryptographic keys.
Архитектурный принцип: данные off-chain, контроль on-chain
Хранить медицинские записи непосредственно в блокчейне — ошибочное решение по нескольким причинам. Во-первых, HIPAA, GDPR и большинство national health data laws требуют возможности удаления данных — это несовместимо с immutability блокчейна. Во-вторых, размер данных (изображения, лабораторные результаты, видео) делает on-chain хранение экономически нецелесообразным. Правильная архитектура:
- On-chain: ссылки на данные (content-addressed hash), права доступа, audit log, consent записи
- Off-chain: зашифрованные медицинские данные в HIPAA-compliant storage (S3, Azure Health Data Services) или децентрализованном хранилище (Ceramic, Filecoin с шифрованием)
Шифрование и key management
Ключевая идея: данные зашифрованы симметричным ключом (AES-256). Этот data encryption key (DEK) зашифрован публичным ключом пациента. Для предоставления доступа врачу — DEK перешифровывается публичным ключом врача (proxy re-encryption).
Медицинская запись → зашифровать AES-256 → зашифрованные данные (в IPFS/Filecoin)
DEK → зашифровать публичным ключом пациента → encrypted DEK (в смарт-контракте)
Доступ врача:
encrypted DEK → proxy re-encryption → encrypted DEK for doctor
Врач расшифровывает своим приватным ключом → DEK → расшифровывает данные
Proxy re-encryption (библиотеки: NuCypher/Threshold Network, литература: PRE схемы) позволяет делегировать доступ без раскрытия оригинального ключа. Это лучший подход для медицинских систем: пациент выдаёт грант доступа врачу на конкретный период и конкретные записи.
Smart contract архитектура
EHR Registry (Electronic Health Records)
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 => indices
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;
// Оффчейн: сервис удаляет зашифрованные данные из storage
// и уничтожает DEK
emit RecordDeactivated(patientId, recordId);
}
HL7 FHIR совместимость
Для интеграции с существующими медицинскими системами — данные хранятся в формате FHIR (Fast Healthcare Interoperability Resources) JSON. FHIR ресурсы: Patient, Observation, DiagnosticReport, Condition, MedicationRequest.
Структура хранения: FHIR JSON → AES-256 encrypt → IPFS → content hash on-chain. При получении доступа: расшифровать → parse FHIR JSON → преобразовать в нужный формат.
DID (Decentralized Identifiers)
Пациенты и провайдеры идентифицируются через DID (W3C стандарт) вместо raw Ethereum адресов. Это обеспечивает key rotation (смена ключей без потери identity) и cross-system interoperability.
did:ethr:0x742d35... — DID на основе Ethereum адреса
did:web:hospital.example.com — DID на основе domain
did:key:z6Mkf... — DID на основе public key
Интеграция с провайдерами здравоохранения
Для больниц и клиник — интеграция через FHIR API существующих EMR (Electronic Medical Records) систем: Epic, Cerner, Meditech. Adapter сервис: читает из EMR → конвертирует в стандартный формат → шифрует → публикует on-chain.
| Компонент | Технология |
|---|---|
| Smart contracts | 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) |
| Indexing | The Graph |
Сроки и стоимость
| Фаза | Содержание | Срок |
|---|---|---|
| Архитектура | DID scheme, FHIR mapping, threat model | 1-2 нед |
| Core contracts | Registry, consent, audit | 3-4 нед |
| Encryption layer | Key management, proxy re-encryption | 2-3 нед |
| Storage integration | IPFS/Filecoin, FHIR parser | 2-3 нед |
| Provider integration | FHIR API adapter | 2-4 нед |
| Frontend | Patient portal, provider UI | 3-4 нед |
| Security audit | Контракты + crypto implementation | 2-4 нед |
Полная production-ready система: 4-6 месяцев. MVP без proxy re-encryption и FHIR интеграции: 2-3 месяца.







