Если вы видите это, значит, я еще не придумал, что написать.
SQL-инъекции представляют серьезную угрозу для безопасности веб-приложений. В этой статье мы рассмотрим, как можно реализовать метод для проверки параметров запроса на наличие SQL-инъекций в Laravel, используя более универсальный и надежный подход.
Создание класса для проверки SQL-инъекций Сначала создадим класс AdvancedSqlInjectionChecker, который будет содержать метод для проверки строк на наличие SQL-инъекций.
class AdvancedSqlInjectionChecker
{
/**
* Проверяет, содержит ли строка потенциально опасные SQL-инъекции.
*
* @param string $input
* @return bool
*/
public static function hasSqlInjection(string $input): bool
{
// Набор шаблонов для обнаружения SQL-инъекций
$patterns = [
'/(?:\b(select|union|insert|update|delete|drop|alter|create|truncate)\b)/i', // Ключевые слова SQL
'/(?:--|\#|\;)/', // Комментарии и точка с запятой
'/(?:\b(and|or|xor|not)\b\s+[\w\s]+\s*(=|like|>|<|in|is|between)\s+[\w\s]+)/i', // Условные операторы
'/(?:\b(?:exec|execute|sp_executesql|xp_cmdshell)\b)/i', // Команды выполнения
'/(?:\b(select|union)[\s\S]+(from|join|into|load_file|information_schema|mysql)\b)/i', // Комбинированные шаблоны
];
foreach ($patterns as $pattern) {
if (preg_match($pattern, $input)) {
return true;
}
}
// Дополнительная проверка на символы, которые часто используются в инъекциях
$specialChars = ['\'', '"', ';', '\\', '--', '#'];
foreach ($specialChars as $char) {
if (str_contains($input, $char)) {
return true;
}
}
return false;
}
}
Проверка параметров запроса в Laravel
Теперь интегрируем наш класс проверки в Laravel. Мы создадим Middleware, который будет проверять все параметры запроса на наличие SQL-инъекций.
Создадим новый Middleware:
php artisan make:middleware CheckForSqlInjection
Внутри созданного Middleware реализуем проверку:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use AdvancedSqlInjectionChecker;
class CheckForSqlInjection
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
$allInputs = array_merge($request->all(), $request->route()->parameters());
foreach ($allInputs as $key => $value) {
if (is_string($value) && AdvancedSqlInjectionChecker::hasSqlInjection($value)) {
return response()->json(['error' => 'Potential SQL Injection detected in parameter: ' . $key], 400);
}
}
return $next($request);
}
}
Зарегистрируем Middleware в app/Http/Kernel.php:
protected $middleware = [
// ...
\App\Http\Middleware\CheckForSqlInjection::class,
];
Создадим юнит-тест для проверки работы класса AdvancedSqlInjectionChecker.
Создадим тестовый класс:
php artisan make:test AdvancedSqlInjectionCheckerTest
Реализуем тесты внутри созданного класса:
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use App\Services\AdvancedSqlInjectionChecker;
class AdvancedSqlInjectionCheckerTest extends TestCase
{
/**
* @dataProvider sqlInjectionProvider
*/
public function testHasSqlInjection($input, $expected)
{
$this->assertEquals($expected, AdvancedSqlInjectionChecker::hasSqlInjection($input));
}
public function sqlInjectionProvider()
{
return [
["SELECT * FROM users;", true],
["' OR 1=1 --", true],
["DROP TABLE users;", true],
["Safe string", false],
["12345", false],
];
}
}
Мы создали класс AdvancedSqlInjectionChecker, который проверяет строки на наличие SQL-инъекций с помощью регулярных выражений и анализа специальных символов. Затем мы интегрировали эту проверку в Laravel с помощью Middleware, который анализирует все параметры входящих запросов.
Этот метод позволяет обнаруживать потенциальные SQL-инъекции на раннем этапе и предотвращать выполнение опасных запросов. Однако, для полной защиты рекомендуется использовать подготовленные выражения и экранирование данных.
Пример использования Допустим, у нас есть контроллер, который принимает параметры запроса:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UserController extends Controller
{
public function getUser(Request $request)
{
$name = $request->input('name');
$email = $request->input('email');
// Допустим, здесь мы используем Eloquent для поиска пользователя
$user = \App\Models\User::where('name', $name)->where('email', $email)->first();
return response()->json($user);
}
}
С включенным Middleware CheckForSqlInjection, каждый параметр запроса будет проверяться на наличие SQL-инъекций перед выполнением основного кода контроллера. Это добавляет дополнительный уровень безопасности вашему приложению.
Если вы видите это, значит, я еще не придумал, что написать.
Пайплайны предоставляют элегантное решение для последовательного применения множества фильтров к запросу Eloquent. В Laravel пайплайны позволяют инкапсулировать логику фильтрации в отдельные классы и затем последовательно применять их к запросу. Это повышает читаемость, тестируемость и расширяемость кода.
Давайте рассмотрим, как можно использовать пайплайны для реализации фильтров в Eloquent:
Каждый фильтр будет отдельным классом, реализующим интерфейс FilterInterface.
<?php
namespace App\Pipelines\Filters;
use Illuminate\Database\Eloquent\Builder;
interface FilterInterface
{
public static function apply(Builder $builder, $value): Builder;
}
Примеры фильтров:
Фильтр по названию:
<?php
namespace App\Pipelines\Filters;
use Illuminate\Database\Eloquent\Builder;
class NameFilter implements FilterInterface
{
public static function apply(Builder $builder, $value): Builder
{
return $builder->where('name', 'like', '%' . $value . '%');
}
}
Фильтр по категории:
<?php
namespace App\Pipelines\Filters;
use Illuminate\Database\Eloquent\Builder;
class CategoryFilter implements FilterInterface
{
public static function apply(Builder $builder, $value): Builder
{
return $builder->where('category', $value);
}
}
Фильтр по цене:
<?php
namespace App\Pipelines\Filters;
use Illuminate\Database\Eloquent\Builder;
class PriceFilter implements FilterInterface
{
public static function apply(Builder $builder, $value): Builder
{
return $builder->whereBetween('price', [$value['min'], $value['max']]);
}
}
Создайте класс ProductPipeline, который будет последовательно применять фильтры.
<?php
namespace App\Pipelines;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Pipeline;
class ProductPipeline
{
protected array $filters = [];
public function __construct(array $filters)
{
$this->filters = $filters;
}
public function apply(Builder $builder): Builder
{
return app(Pipeline::class)
->send($builder)
->through($this->filters)
->thenReturn();
}
}
Используйте пайплайн в контроллере для фильтрации данных.
<?php
namespace App\Http\Controllers;
use App\Models\Product;
use App\Pipelines\Filters\NameFilter;
use App\Pipelines\Filters\CategoryFilter;
use App\Pipelines\Filters\PriceFilter;
use App\Pipelines\ProductPipeline;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
/**
* Class ProductController
*/
class ProductController extends Controller
{
/**
* Получение списка продуктов с фильтрацией
*
* @param Request $request
* @return JsonResponse
*/
public function index(Request $request): JsonResponse
{
$pipeline = new ProductPipeline([
NameFilter::class,
CategoryFilter::class,
PriceFilter::class,
]);
$products = $pipeline->apply(Product::query())->get();
return response()->json($products);
}
}
Создайте FormRequest для валидации входящих данных.
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
/**
* Class ProductFilterRequest
*/
class ProductFilterRequest extends FormRequest
{
/**
* Правила валидации
*
* @return array
*/
public function rules(): array
{
return [
'name' => 'nullable|string|max:255',
'category' => 'nullable|string|max:255',
'price' => 'nullable|array',
'price.min'=> 'nullable|numeric|min:0',
'price.max'=> 'nullable|numeric|min:0',
];
}
}
Использование пайплайнов для фильтрации данных в Eloquent позволяет легко и элегантно управлять сложными запросами. Это улучшает читаемость кода и упрощает его поддержку, предоставляя более гибкий и модульный способ обработки запросов.
{message}