Зачем нам нужно указывать тип параметра в bindParam ()?

Я немного запутался, почему нам нужно указывать тип данных, которые мы передаем в функцию bindParam () в PDO в Php. Например этот запрос:

$calories = 150; 
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
FROM fruit
WHERE calories < ? AND colour = ?');
$sth->bindParam(1, $calories, PDO::PARAM_INT); 
$sth->bindParam(2, $colour, PDO::PARAM_STR, 12);
$sth->execute();

Есть ли угроза безопасности, если я не укажу 3-й параметр. Я имею в виду, если я просто делаю в bindParam ():

$sth->bindParam(1, $calories); 
$sth->bindParam(2, $colour);

Ответы на вопрос(3)

Я знаю, что это старый вопрос, но я хотел немного покопаться, так как я всегда забываю внутреннюю работу параметра типа данных при привязке значений / параметров. Кроме того, у меня значительно больше опыта работы с MySQL, поэтому этот ответ будет больше связан с MySQL.

MySQL автоматически конвертирует числа в строки по мере необходимости, и наоборот, так что если вы делаете:

SELECT * FROM tablename WHERE columnname = 42

или же

SELECT * FROM tablename WHERE columnname = '42'

MySQL знает, какой тип столбцаcolumnname должен быть и будет автоматически набирать приведение, если в качестве значения указан неправильный тип.

Теперь, глядя наИсходный код для PDOStatement:

if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_STR && param->max_value_len param_type) == PDO_PARAM_INT && (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE)) {
    convert_to_long(parameter);
} else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL && Z_TYPE_P(parameter) == IS_LONG) {
    convert_to_boolean(parameter);
}

Вы'Заметим из приведенного выше кода, что когда третий параметр в операторах связывания либо пропущен, либоPDO_PARAM_STR (значение по умолчанию), PHP наберет приведение значения к строке. Когда это предоставляется какPDO_PARAM_INTв отличие от того, что вы можете ожидать, это делаетне получить тип, приведенный к целому числу, но вместо этого PHP приводит его кдолго! Так что если вы делаете:

$stmt->bindValue("integer_column", "41.9", PDO_PARAM_INT);

База данных фактически получит длинное значение41.9 для использования в запросе, который затемзакругленный (по крайней мере, MySQL) до ближайшего целого числа, поэтому последнее значение используется в приведенном выше запросе для столбца типаINTEGER будет .42

mysql> SELECT CAST(41.9 AS UNSIGNED);
+------------------------+
| CAST(41.9 AS UNSIGNED) |
+------------------------+
|                     42 |
+------------------------+

То же самое верно, если у вас есть нечисловая строка, например:"41.9asdf"

$sth->bindValue("integer_column_value", "41.9asdf", PDO_PARAM_INT);

Сначала он будет приведен к типу long с помощью PHP (41.9), а затем это значение будет округлено до целого числа MySQL (42).

Как я упоминал ранее, MySQL автоматически преобразует строки в числа, поэтому, если вы передадите MySQL строку"123", у него не будет проблем с преобразованием этого в целое число или любой другой числовой тип. Однако имейте в виду, что когда MySQL преобразует строку в целое число,усекает вместо раундов, так что это может быть реальной разницей между предоставлением типа при связывании.

mysql> SELECT CAST('123.6' AS UNSIGNED);
+---------------------------+
| CAST('123.6' AS UNSIGNED) |
+---------------------------+
|                       123 |
+---------------------------+

mysql> SELECT CAST(123.6 AS UNSIGNED);
+-------------------------+
| CAST(123.6 AS UNSIGNED) |
+-------------------------+
|                     124 |
+-------------------------+

Итак, вот значения, которые будет содержать целочисленный столбец в PHP для следующего:

$stmt->bindValue("integer_column", "123.6", PDO_PARAM_INT); // 124
$stmt->bindValue("integer_column", "123.6"); // 123
$stmt->bindValue("integer_column", (int) "123.6"); // 123
$stmt->bindValue("integer_column", round("123.6")); // 124

Обратите внимание, как вы можете заметить из вышесказанного, PHP преобразует строку в целое числопо-другому чем как MySQL делает. MySQL раундов плавает, но PHP будет использоватьfloor значение:

var_dump((int) "123.6"); // int(123)
var_dump((int) 123.6); // int(123)

Теперь, чтобы фактически ответить на аспект безопасности вашего вопроса, нет никакой выгоды для безопасности в предоставлении третьего параметра для подготовленных утверждений. Подготовленных операторов сами по себе достаточно для смягчения внедрения SQL, если все сделано правильно (см.эта ссылка для некоторых непонятных крайних случаев).

Если вы не укажете тип, параметр будет по умолчанию связан как строка. Затем вы можете получить запрос, эквивалентный:

SELECT foo FROM bar WHERE baz = '42'

Это может или не может быть проблемой. Как и PHP и другие языки программирования, базы данных имеюттипы и правила дляКастинг между типами. MySQL обычно будет неявно приводить числовые строки к числам по мере необходимости. Некоторые базы данных являются более строгими, чем другие, и для определенных операций требуется правильный тип. В большинстве случаев это мало что меняет. Но если вам действительно нужно пройти 42 как42 и не'42', вам нужно явно связать его как.INT

Решение Вопроса

С помощьюbindParam() с типами можно было бы считать более безопасными, потому что это позволяет проводить более строгую проверку, дополнительно предотвращая SQL-инъекции. Тем не менее, я бы нене сказать, что естьреальный риск безопасности, если вы нетак делать, так как это больше тот факт, что вы делаетеподготовленное заявление это защищает от SQL-инъекций, чем проверка типа. Более простой способ добиться этого - просто передать массив вexecute() функция вместо использованияbindParam(), как это:

$calories = 150; 
$colour = 'red';

$sth = $dbh->prepare('SELECT name, colour, calories
                      FROM fruit
                      WHERE calories < :calories AND colour = :colour');

$sth->execute(array(
    'calories' => $calories,
    'colour' => $colour
));

Вы'Вы не обязаны использовать словарь, вы также можете сделать это так же, как вы делали с вопросительными знаками, а затем поместить его в том же порядке в массиве. Тем не менее, даже если это работает отлично, яя рекомендую использовать привычку использовать первый, так как этот метод беспорядок, когда вы достигнете определенного количества параметров. Ради полноты здесьКак это выглядит:

$calories = 150; 
$colour = 'red';

$sth = $dbh->prepare('SELECT name, colour, calories
                      FROM fruit
                      WHERE calories < ? AND colour = ?');

$sth->execute(array($calories, $colour));
 Sameer Zahid02 июн. 2013 г., 11:43
Спасибо, я провела некоторые тесты слишком локально, и да, инъекция нет работа. Спасибо!
 PLPeeters02 июн. 2013 г., 10:49
Подготовленные заявления Помогите избежать инъекций MySQL, подготовив свою сторону сервера запросов и затем привязав к ней свои параметры. Ваш способ сделать это не былНеправда, я просто показывал вам более простой способ сделать это, по крайней мере, на мой взгляд.
 Sameer Zahid02 июн. 2013 г., 10:46
В старых функциях mysql_x, если мы использовали $ id без выполнения (int) $ id; и даже если мы использовали mysql_real_escape_string ($ id), мы все равно можем получить SQL-инъекцию. Но так ли это в PDO?
 PLPeeters02 июн. 2013 г., 11:03
Как я уже говорил выше, этоВ этом весь смысл подготовленного оператора, который позволяет избежать внедрения SQL. То, как вы привязываете свои параметры к вашему запросу, неэто действительно важно. Просто чтобы быть уверенным, я попытался ввести мою форму входа на мой сайт, который я изменил, чтобы использоватьbindParam() без указания типа. Это нене работает, так что тыбезопасно, даже если выне указывать тип :)
 Sameer Zahid02 июн. 2013 г., 10:55
Я понимаю, что оба способа верны. Я хочу знать, что, например, если злоумышленник предоставит значение id, например: $ _POST ['Я бы'знак равно1 или 3 = 3 ', Это будет всегда верно, но если мы сделаем это: $ id = (int) $ id; Вышеуказанная атака не может произойти. Итак, я хочу знать, что в PDO я могу просто поместить $ _POST ['Я бы'] в функции bindParam ()? не уточняя, что этоЯ бы' такое интергер?
 PLPeeters02 июн. 2013 г., 12:59
Вы'Добро пожаловать Дон»не забудьте подтвердить мой ответ, если он действительно решил ваш вопрос :)

Ваш ответ на вопрос