Защищают ли подготовленные выражения/переменные полностью от 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 прекрасно справляется с инъекциями, просто в качестве побочного эффекта.