Страницы

Поиск по вопросам

суббота, 7 декабря 2019 г.

Защита от SQL иньекций в php

#php #sql #json #sql_injection


Пишу API, требуется обезопасить его от SQL инъекций. Как лучше и проще защититься от них?

Данные приходят в формате JSON и обрабатываются функцией json_decode(); После чего:


Используются в запросах как числа: "... WHERE id='{$id}' ". Требуется функция, которая
проверит, что это $id целое число. Или вернет число, к примеру: $id = real_int($id);
В результате должно состоять из знака минуса или символов 0-9.
Используются как строки: "... '{$text}' ... ".  По идее тут достаточно заменить '
на \', хотя помнится были варианты обхода такой защиты. В общем тоже нужна функция,
которая надежно защитит от инъекций.
Используются как список идентификаторов: "... WHERE id IN({$ids}) ". Полагаю их проще
распарсить и каждый id обработать функцией из первого пункта.

    


Ответы

Ответ 1



Разумеется, вся работа с чистым SQL должна производиться только через плейсхолдеры. Конечно, более предпочтительным вариантом является ORM (идеальный вариант для примитивных запросов, нужных автору) либо прочие квери билдеры - в этом случае пользователь РНР не видит SQL совсем, и следовательно, инъекцию при всем желании устроить не может. Но поскольку новичков эти слова обычно пугают до смерти, то этот вариант подходит только опытным пользователям. При работе же с чистым SQL, повторюсь, любые данные должны попадать в запрос только через плейсхолдеры. Разумеется, защита от инъекций должна производиться способом, отличным от "мы тут сейчас на жувачку прилепим пару функций, там из гуана и палок сварганим другую, потом наваяем ещё кода - и все полетит!". Защита должна быть единообразной и систематической. Разумеется, слой работы с БД должен быть отделен от слоя работы с прикладными данными, и предоставлять удобные и безопасные методы для работы с БД, чтобы не приходилось в бизнес-логике видеть какие-то непонятные вложенные по 30 раз друг в друга array_map, implode, intval и пр. ORM здесь опять на первом месте, а сырой SQL, по-хорошему, имеет смысл использовать только для сложных запросов. Но даже и для чистого SQL DB-layer обязан предоставлять безопасные методы, основанные на плейсхолдерах. Разумеется, в общем случае, использование PDO - это на самом деле, не лишний код, а сокращение кода. Поскольку PDO - единственный из драйверов mysql в РНР, который является высокоуровневой абстракцией, позволяющей сокращать рутинные операции. Разумеется, "implode(',', $ids) в подготовленных запросах" НИКАК НЕ спасёт ситуацию, а лишь вернёт неверные данные. Разумеется, все эти прекрасные рассуждения разбиваются о простой случай с WHERE id IN()... Можно, конечно, наваять кода, как предлагается по ссылке в комментариях. Но это, на самом деле, будет профанация. Нам не нужны на самом деле эти плейсхолдеры для каждого значения. Нам нужен способ тупо безопасно поместить массив в запрос, не перемешивая бизнес-логику с логикой защиты от инъекций. Для этого надо просто распространить практику работы с плейсхолдерами на несколько дополнительных типов данных. Этим и должен заниматься DB-layer. поскольку ПДО, увы, не предоставляет нужного функционала, можно воспользоваться другими библиотеками. Такими как DBSimple или Safemysql. В обоих случаях мы сможем решить все поставленные выше задачи. Вот, к примеру, для safemysql: Нужен запрос с WHERE id=1 с проверкой на int? $name = $db->getOne("SELECT name FROM users WHERE id=?i", $id); Будет выброшена ошибка, если в $id - не число. Нужен запрос со строковой переменной? $row = $db->getRow("SELECT * FROM users WHERE email=?s", $email); Переменная $email будет корректно отформатирована, и никакой мусор в ней не приведет к инъекции. Нужен запрос с пресловутым WHERE id IN()? Ради бога - всего лишь указываем нужный тип: $data = $db->getAll("SELECT * FROM users WHERE id IN(?a)", $ids); И опять драйвер сам отформатирует массив так, что инъекция не пройдет. Но сделает это все незаметно для пользователя. В этом и состоит смысл программирования - делегировать различные задачи разным сервисам, чтобы каждый занимался своим делом. Чтобы "лишний код" был инкапсулирован в свой сервис, а не писался в одной большой куче, как это принято в похапе. Нужно добавить в БД строку прямиком из джейсона? Нет проблем. $json = '{"name":"John","lastname":"Doe"}'; $db->query("INSERT INTO table SET ?u", json_decode($json,true)); здесь будут правильно отформатированы для запроса не только данные, но и имена полей, про которые обычно вообще не думают, а если и думают, то форматируют все равно неправильно.

Комментариев нет:

Отправить комментарий