Для полів, витік яких особливо шкідливий (токени сторонніх API, секрети 2FA, номери документів, медичні нотатки), Laravel має касти, що шифрують значення на рівні застосунку:
protected function casts(): array
{
return [
'two_factor_secret' => 'encrypted',
'api_credentials' => 'encrypted:array',
'medical_notes' => 'encrypted',
'settings' => AsEncryptedArrayObject::class,
];
}
У базі зберігається зашифрований рядок (AES-256 з APP_KEY і MAC для перевірки цілісності). Модель автоматично шифрує при записі й розшифровує при читанні.
Від чого це захищає:
- дамп чи бекап бази потрапив не в ті руки;
- доступ до бази без доступу до застосунку (SQL-ін'єкція, що читає таблиці, скомпрометований обліковий запис адміністратора бази, репліка для аналітики);
- логи запитів до бази містять лише шифротекст.
Від чого не захищає: від того, хто має доступ і до бази, і до APP_KEY (скомпрометований сервер застосунку), і від вразливостей у самому застосунку, що показують розшифровані дані.
Обмеження, про які треба знати:
- не можна шукати й сортувати:
where('passport_number', $value)не знайде запис - кожне шифрування дає різний шифротекст (випадковий IV). Якщо пошук потрібен - окрема колонка з «сліпим індексом»: HMAC від значення з окремим ключем, і пошук за ним; - не можна індексувати, агрегувати, використовувати в
unique-обмеженнях бази; - розмір колонки: шифротекст (base64 з IV і MAC) значно довший за значення - колонка
text, а неvarchar(50); - ротація
APP_KEYвимагає перешифрування всіх таких полів; без старого ключа вAPP_PREVIOUS_KEYSдані втрачено; - продуктивність: розшифрування при кожному читанні - помітно на великих вибірках.
Окремий ключ для шифрування даних: можна вказати власний шифрувальник для моделей (Model::encryptUsing($encrypter)), щоб ключ даних був окремо від APP_KEY і ротувався незалежно.
Не шифрувати те, що має бути хешованим: паролі й одноразові токени - хеш (їх не потрібно розшифровувати), а шифрування - для даних, які застосунку треба прочитати у відкритому вигляді.