Rails 4 шифрует внешний ключ
Я создаю приложение, которое требует соответствия HIPAA, что означает, что я не могу допустить, чтобы определенные соединения были свободно доступны для просмотра в базе данных (пациенты и рекомендации для них).
Эти таблицы связаны через patients_recommendations
таблица в моем приложении, которая работала хорошо, пока я не добавил шифрование через attr_encrypted. В попытке сократить объем шифрования и дешифрования (и связанные с этим издержки) я хотел бы иметь возможность просто зашифровать patient_id
в patients_recommendations
Таблица. Однако после изменения типа данных на string
и имя столбца encrypted_patient_id
приложение разрывается со следующей ошибкой при попытке повторного заполнения базы данных:
не может написать неизвестный атрибут `Patient_id'
Я предполагаю, что это потому, что объединение ищет столбец напрямую, а не просматривая модель (имеет смысл использовать модель, вероятно, медленнее). Есть ли способ, которым я могу заставить Rails пройти модель (где attr_encrypted
добавил необходимые вспомогательные методы)?
Обновить:
В попытке найти обходной путь, я попытался добавить before_save к модели следующим образом:
before_save :encrypt_patient_id
...
private
def encrypt_patient_id
self.encrypted_patient_id = PatientRecommendation.encrypt(:patient_id, self.patient_id)
self.patient_id = nil
end
Это также не работает, что приводит к той же ошибке unknown attribute
, Любое решение будет работать для меня (хотя первое будет касаться основной проблемы), любые идеи, почему before_save
не вызывается при создании через ассоциацию?
2 ответа
Вероятно, вам следует хранить данные PII и данные PHI в отдельных БД. Зашифруйте данные PII (включая любые связи с поставщиком или местоположением поставщика), а затем хэшируйте все данные PHI, чтобы упростить их. Пока нет прямой связи между ними, было бы приемлемо не шифровать данные PHI, поскольку они анонимны.
План А
Не установлен patient_id
в nil
в encrypt_patient_id
поскольку его не существует и проблема может уйти.
Кроме того, завершение обратного вызова с nil
или же false
остановит цепочку обратного вызова, выставит объяснение true
в конце метода.
План Б, переосмыслить ваши варианты
Существуют и другие варианты - от прозрачного шифрования на уровне базы данных (которое формально шифрует данные на диске) до зашифрованных файловых систем для хранения определенных табличных пространств и выравнивания шифрования данных в столбцах.
Шифрование столбцов соединения звучит как дорога к несчастью по разным причинам - от проблем с отчетами до проблем производительности, когда необходимо объединение, которое может быть довольно серьезным,
проблема, которую вы сейчас испытываете с начальным числом, выглядит так, как будто это первый удар, вызванный этим на плохой дороге (в этом случае activerecord, похоже, не понимает, как справиться с вашей ассоциацией, он пытается установить patient_id
на инициализацию и разрывы).
Затраты на шифрование данных с ограничениями могут быть не такими высокими, как вы думаете, не зная, как обстоят дела с HIPAA, но для PCI вам точно не рекомендуется отображать защищенные данные на экране, поэтому шифрование сопряжено с небольшими накладными расходами, поскольку это происходит относительно редко. (бизнес-нужно знать и т. д.).
Кроме того, память, вероятно, считается "не в состоянии покоя и не в пути", теоретически вы можете кэшировать некоторые чистые значения в течение ограниченных периодов времени и, таким образом, сэкономить на издержках дешифрования.
По сути, шифрование данных может быть не таким уж плохим, а шифрование ключей в базе данных может быть хуже, чем вы думаете
Я предлагаю поговорить напрямую, я занимаюсь вопросами соответствия PCI DSS, и эта тема меня интересует.
Опция: односторонние хеши для первичных / внешних ключей
PatientRecommendation
будет иметь хэш patient_id
- назови это patient_hash
а также Patient
будет способен генерировать то же самое patient_hash
от его id
- но я бы посоветовал хранить patient_hash
в обеих таблицах, для Patient
это был бы первичный ключ для соединения и для PatientRecommendation
это был бы внешний ключ для соединения,
таким образом, вы определяете отношение рельсов в этих терминах, и рельсы больше не будут путать вашу схему отношений
has_many :patient_recommendations, primary_key: :patient_hash, foreign_key: :patient_hash
и результат криптографически более надежен и прост для обработки базы данных
ЕСЛИ ты непреклонен в том, чтобы не хранить patient_hash
в Patient
Вы можете использовать простой SQL-оператор, чтобы сделать отношение - менее удобное, но работоспособное - что-то в строках этого псевдосклика:
JOIN ON generate_hash(patient.id) = patient_recommendations.patient_hash
Oracle, например, имеет возможность создавать функциональные индексы (думаю, create index generate_hash(patient.id)
), поэтому этот подход может быть довольно эффективным в зависимости от вашего выбора базы данных.
Тем не менее, игра с клавишами соединения значительно усложнит вашу жизнь, даже с этими мерами
Я расскажу об этом посте позже с дополнительными опциями