Уже давно меня мучает вопрос: Как защитить свой сайт от 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();
Комментариев нет:
Отправить комментарий