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

177
19 декабря 2016, 19:40

Уже давно меня мучает вопрос: Как защитить свой сайт от 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 для работы с базой данных", подключение такое же, как и указано в статье.

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

Answer 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
Answer 2

Все правильно: в таблицу и должна добавляться строка «Infor`mat'ion». Экранирование должно происходить именно в момент запроса, а не храниться в таком виде. И PDO прекрасно с этим справляется. И mysql_real_escape_stringmysqli_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_replace — небезопасны (\xbf\x27 и другие проблемы), используйте MySQL только в режиме настоящих подготовленных запросов! PDO нужно правильно использовать (спасибо @romeo за ссылку).

Answer 3

PDO, и его prepare.

$temp = $mysql->prepare('DELETE FROM `unigies` WHERE `idUser` = :idUser Limit 1;');
$temp->bindParam(':idUser', $id, PDO::PARAM_INT);
$temp->execute();
READ ALSO
Запустить задачу в фоне php

Запустить задачу в фоне php

Есть веб-интерфейс, куда юзер загружает файл и файл процесситсяНужно чтобы после загрузки началась обработка файла и пользователь в реальном...

201
Как объединить вывод данных с значением false и если пустое?

Как объединить вывод данных с значением false и если пустое?

Проблема в том что у меня в базе данных есть значение одного столбца, оно обычно false, но бывает случается что пустое, как мне при выводе объединить...

161
Bootstrap меню на Wordpress

Bootstrap меню на Wordpress

Я создал следующие меню на bootstrap и перенёс его в Wordpress, но оно почему то оно не работает? Помогите исправить

346
Что значит на ООП PHP запись: $this->name->name2->name3->save();?

Что значит на ООП PHP запись: $this->name->name2->name3->save();?

$this->name — свойство, но что дальше? Те

158