Коли запит до бази даних завершується помилкою, Laravel створює виняток QueryException, повідомлення якого містить SQL-код з усіма підставленими значеннями параметрів. Це зроблено навмисно: повідомлення на кшталт SQL: insert into "users" ("email") values (?) майже нічого не говорить про те, який саме рядок спричинив проблему, тоді як версія з реальними значеннями дає миттєву відповідь.
Проблема безпеки даних
Проблема полягає в тому, що таке повідомлення не залишається там, де ви його бачите. Це рядок у винятку, а винятки записуються в різні місця.
Розгляньмо типовий приклад:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'ada@example.com' for key 'users_email_unique' (Connection: mysql, Host: 10.0.4.17, Port: 3306, Database: platform, SQL: insert into `users` (`email`, `name`, `national_id`) values (ada@example.com, Ada Lovelace, 640312-4185))
Кожне значення, передане в цей запит insert, тепер знаходиться в рядку логу. Якщо запит виконувався всередині задачі черги, той самий рядок потрапляє в колонку exception таблиці failed_jobs, оскільки метод DatabaseFailedJobProvider::log() перетворює виняток на рядок і вставляє його. Якщо ви використовуєте APM або агент OpenTelemetry, він записав виняток у span. Будь-яке місце, куди ваша система звітування про помилки надсилає дані, тепер зберігає копію всіх цих параметрів: email-адреси, імена, державні ідентифікатори, API-токени, що записуються в таблиці інтеграцій.
Рішення в Laravel 13.27
Laravel 13.27 додає опцію на рівні з'єднання з базою даних, яка зупиняє інтерполяцію значень у повідомлення про помилки.
Як увімкнути маскування
Встановіть параметр mask_bindings_in_exception_messages для з'єднання:
'connections' => [
'mysql' => [
'driver' => 'mysql',
// ...
'mask_bindings_in_exception_messages' => env('DB_MASK_BINDINGS', false),
],
],
Цей ключ входить до стандартного файлу config/database.php фреймворку для всіх п'яти стандартних з'єднань, тому додаток, який ніколи не публікував цей конфігураційний файл, може увімкнути маскування лише через змінну оточення:
DB_MASK_BINDINGS=true
Результат маскування
Після увімкнення цієї опції повідомлення зберігає свої заповнювачі ?:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'ada@example.com' for key 'users_email_unique' (Connection: mysql, Host: 10.0.4.17, Port: 3306, Database: platform, SQL: insert into `users` (`email`, `name`, `national_id`) values (?, ?, ?))
Таким чином, чутливі дані користувачів не потрапляють у логи, системи моніторингу та інші місця, де зберігаються повідомлення про помилки. Це особливо важливо для відповідності вимогам захисту персональних даних та безпеки додатків.
Додаткові ресурси
Для глибшого розуміння теми варто ознайомитися з повним оглядом Laravel 13.27, документацією про налагодження та логування в Laravel-додатках, а також інструментами для виявлення персональної інформації в тексті (Privacy Filter) та клонування продакшн-баз даних з анонімізованими даними (Clonio CLI).