Страницы

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

вторник, 26 ноября 2019 г.

Грамотная защита от SQL-Injection


Уже давно меня мучает вопрос: Как защитить свой сайт от SQL-Injection. В былые времен
я использовал mysql_real string_escape для экранирования кавычек. Но прошло некое время
и я узнал про PDO, говорится, мол, эта тема неплохо поможет в защите сайта. Скажем, я не великий хакер и никак это не могу проверить, я знаю, что при mysql_real string_escape все кавычки и прочая ерунда экранировались - и вуаля, вы уже в домике. 

Сейчас я работаю над своим проектом и решил проверить сайт на защиту проникновения. В результате, что я делал:

Я просто брал и вводил данные с кавычками, например, Infor`mat'ion. Как я уже говорил
я не великий хакер, но точно знаю, что вся эта дрянь должна экранироваться, но она так и заносилась в базу данных, что натолкнуло меня на мысль, что никакой защиты у меня, по сути, и нету. А данные я заносил вот таким вот образом: 

$sql = "INSERT INTO chat (time, login, private, text) values(?, ?, ?, ?)";
$db->prepare($sql)->execute(array($time, $login, $private, $text));`


Этот метод я вычитал на хабре в "Почему стоит пользоваться PDO для работы с базой данных", подключение такое же, как и указано в статье.

Как можно защититься от этих инъекций?
    


Ответы

Ответ 1



При использовании подготовленных выражений (prepared statement) параметры внешнег происхождения отправляются на сервер отдельно от самого запроса либо автоматически экранируются клиентской библиотекой. Создаём таблицу drop_table. Далее $name = "Evil'); DROP TABLE drop_table;--"; $sql = "INSERT INTO `test` (`name`) values('{$name}');"; $statement = $db->prepare($sql); $statement->execute(); Таблица drop_table успешно удалена. :) Теперь тот же запрос, но только через prepared statement: $sql = "INSERT INTO `test` (`name`) values(?);"; $statement = $db->prepare($sql); $statement->execute([$name]); В таблицу test добавилась запись Evil'); DROP TABLE drop_table;-- UPDATE Таким образом, мы обезопасили себя от SQL injection, но не защитили от XSS. В последнем случае требуется: валидация. К примеру, если необходимо оповестить пользователя о недопустимости введёных им данных либо для логирования; санитизация. Приведение данных к соответствующему типу (int)$age. В PHP существует набор фильтров, созданных именно для таких случаев; использования облегченного языка разметки. Markdown - именно им Вы и пользуетес на хэшкоде, либо можно задействовать старичка bbCode. В идеале заставить не только рядовог пользователя, но и администрацию сайта пользоваться им. В целях оптимизации, (чтобы каждый раз не парсить), можно воспользоваться key-value storage (memcached, redis,...) для хранения html версии. Если по каким-то причинам требуется хранить в базе html от источника, который н вызывает доверие, то можно воспользоваться HTML Purifier. Немного рекламы: :) для валидации: Rock Validate для санитизации: Rock Sanitize

Ответ 2



Все правильно: в таблицу и должна добавляться строка «Infor`mat'ion». Экранировани должно происходить именно в момент запроса, а не храниться в таком виде. И PDO прекрасн с этим справляется. И mysql_real_escape_string (и mysqli_real_escape_string) — тоже И даже str_replace — это тоже способ защититься. (См. UPD) То новое, что нам дает PDO, так это слежение за типами передаваемых данных и разграничение действия и данных. Популярная ошибка, которая приводила к плачевным результатам заключалась в том, что некоторые забывали проверять нестроковые типы: $q = "SELECT * FROM `table` WHERE `id` = ".$_GET['post_id']; Разница налицо: $q = "SELECT * FROM `table` WHERE `id` = ".intval($_GET['post_id']); Или просто всегда обрабатывай входные данные, следи за кавычками, за возможными представлениями чисел, за представлениями bool-данных... Или просто положись на готовую обертку, к примеру, PDO. :) UPD: WARNING! mysqli_real_escape_string (вероятно), mysql_real_escape_string, а тем более str_replac — небезопасны (\xbf\x27 и другие проблемы), используйте MySQL только в режиме настоящих подготовленных запросов! PDO нужно правильно использовать (спасибо @romeo за ссылку).

Ответ 3



PDO, и его prepare. $temp = $mysql->prepare('DELETE FROM `unigies` WHERE `idUser` = :idUser Limit 1;'); $temp->bindParam(':idUser', $id, PDO::PARAM_INT); $temp->execute();

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

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