Защищают ли подготовленные выражения/переменные полностью от SQL инъекций?
1,00
р.
р.
В мире уже давно используются mysqli и PDO. Многие очень активно их пропагандируют: есть подготовленные переменные, всё становится безопасно и прочее. Вот, допустим, есть абстрактный код: $dbh = new PDO("test") $stmt = $dbh->prepare('SELECT * FROM users where username = :username') $stmt->execute(array(':username' => $_REQUEST['username'])) или $dbh = new PDO("test") $sth = $dbh->prepare('SELECT name, colour, calories FROM fruit WHERE calories < ? AND color = ?') $sth->execute(array($_POST['number'], $_POST['color'])) И всё... Этого достаточно и ничего больше не надо делать? Никакие конструкции вида mysqli_real_escape_string (для mysqli) и прочие шаманства? Или все же нет? Собственно, хотелось бы знать, если код выше не защищает, то как делать правильно с запросами с PDO и mysqli (и почему тогда говорят про безопасность)? Какие есть наглядные примеры безопасного исполнения запроса, используя PDO и mysqli? А Если и правда защищает, то... я в шоке))
P.S. Возможно данный вопрос уже рассматривался, не знаю, заранее извините.
Ответ Безопасность Если говорить о числовых и строковых литералах в запросе - то да, защищают. Этого достаточно и ничего больше не надо делать? В общем случае - ничего. В случае с ПДО также желательно еще выставлять кодировку соединения в DSN, но это в любом случае нужно делать. Идея подготовленных выражений не в том, чтобы отправить данные и запрос отдельно, а в том, чтобы добавлением данных в запрос занимался не программист, а драйвер БД. А уж как оно у него там внутри реализовано - дело десятое. Примечание По настоянию уважаемого vp_arth я должен добавить, что существует теоретическая возможность так специально настроить свою систему, чтобы она пропускала инъекции в режиме эмуляции. Для этого потребуется две вещи: специальным образом настроить mysql, задав режим NO_BACKSLASH_ESCAPES использовать двойные кавычки вместо одинарных в качестве ограничителей строк Это был баг в mysql, который был исправлен только недавно. Подробнее можно почитать в этом посте на SO. Ограничения Куда интереснее здесь вопрос, что делать, когда подготовленные выражения использовать невозможно. Как говорилось выше, параметры в запросе можно использовать дла замены только строковых или числовых литералов. Но бывают случаи, когда в запрос надо подставить не данные, а имя столбца. Вот пример такой ситуации с разбором неправильных решений (по-английски): An SQL injection against which prepared statements won't help. Ситуация нечастая, но о ней надо знать и быть к ней готовым. Удобство В конце концов, подготовленные выражения просто удобнее. Сравним олд скул $name = $mysqli->real_escape_sring($_GET['name']) $price = $mysqli->real_escape_sring($_GET['price']) $color = $mysqli->real_escape_sring($_GET['color']) $sql = "SELECT * FROM goods WHERE name='$name' and color='$color' and price > '$price'" $res = $mysqli->query($sql) и PDO prepared statements $stmt = $pdo->prepare("SELECT * FROM goods WHERE name = ? and color = ? and price > ?") $stmt->execute([$_GET['name'],$_GET['price'],$_GET['color']]) -- компактно, аккуратно и безопасно. Мифы про экранирование Отдельное замечание по поводу "конструкций вида mysqli_real_escape_string". В том-то и штука, что в отличие от подготовленных выражений, эти конструкции никакого отношения к защите от SQL инъекций не имеют. Эта конструкция выполняет строго определенную и очень специализированную синтаксическую функцию. применение же её "для защиты от инъекций" гарантированно к такой инъекции и приведет. Пример такого нецелевого использования приведен по ссылке выше, но можно привести и другой, совсем уж дурацкий но от этого еще более наглядный: $id = $mysqli->real_escape_string($_GET['id']) $sql = "SELECT * FROM table WHERE id=$id" Если думать, что функция служит "для защиты от инъекций", то этот код логичен. Однако в реальности он дает возможность приписать дальше практически любой запрос через UNION и получить классическую инъекцию. При этом не надо ударяться и в другую крайность - примененная по назначению, для экранирования спецсимволов в строках, mysqli_real_escape_string прекрасно справляется с инъекциями, просто в качестве побочного эффекта.