Совет по безопасности: Параметризуйте имена параметров!
Этот совет по безопасности, связан с недавно обнаруженной уязвимостью, которую я недавно увидел в Twitter:
The reports of SQL injection’s death are greatly exaggerated!
— Luke Jahnke (@lukejahnke) September 20, 2024
Golang project with 11.4k github stars with a very nice injection via query string parameter names (not values!): GET /api/album?_end=36&_order=DESC&_sort=recently_added&_start=0&SELECT+*+FROM+USER--=123 HTTP/1.1 https://t.co/Tdt26fe9ZW
Основная защита от SQL инъекций (SQLi) — это параметризация пользовательского ввода, позволяющая безопасно внедрять его в SQL запросы. Это стандартная практика, о которой постоянно говорится, и в Laravel это сделать довольно просто. На самом деле, я редко встречаю уязвимости SQLi в приложениях Laravel, потому что Eloquent достаточно мощный.
Параметризация — это стандартная практика, и все привыкли писать запросы в Laravel именно так:
$projects = Project::where('active', true)
->when($request->access_code, fn($query, $accessCode) =>
$query->where('access_code', $accessCode))
->when($request->preview_code, fn($query, $previewCode) =>
$query->orWhere('preview_code', $previewCode))
->where('created_at', '>', now()->subYear())
->get();
Однако часто упускается из виду то, что необходимо экранировать не только значения, но и имена параметров!
Рассмотрим этот код:
$firstKey = collect($request->query())->keys()->first();
$firstValue = $request->query($firstKey);
$projects = Project::where('active', true)
->where($firstKey, $firstValue)
->where('created_at', '>', now()->subYear())
->get();
Это выглядит как упрощение, чтобы не добавлять больше вложенных вызовов when()
, и будет хорошо обрабатывать каждый из подобных URL:
https://example.com/projects?access_code=mellon
https://example.com/projects?preview_code=precious
https://example.com/projects?uuid=7da13f1a-b74c-4b3c-aae5-c311fd866894
Но что, если будет предоставлено:
https://example.com/projects?id=1
access_code
, preview_code
и uuid
будут пропущены, позволяя получить доступ к первому совпавшему проекту. Затем можно перебрать ID...
Это происходит потому, что имя параметра поступает из пользовательского ввода, и ему доверяют вслепую, а доверять пользовательскому вводу никогда нельзя.
При работе с именами баз данных и столбцов таблиц рекомендую использовать список разрешённых имён и использовать значения только внутри списка. Эти вещи редко, если вообще когда-либо, бывают динамическими, и вы должны знать структуру своего приложения.
Если вам необходимо вводить в запросы динамические имена параметров, ограничьте набор символов, чтобы удалить специальные символы, убрать конфиденциальные ключевые слова и т. д. Необходимо максимально ограничить эти значения, так как это приводит к возникновению рисков. (Не говорите, что я вас не предупреждал!)
Статьи о SQL инъекциях:
- SQLi: Что такое SQL-инъекция
- SQLi: Шпаргалка по SQL-инъекциям
- SQL-инъекции: UNION атаки
- SQL-инъекции: Исследование базы данных атаками
- Слепая SQL-инъекция