Как создать безопасный MySQL подготовленный оператор в php?

Я новичок в использовании подготовленных утверждений в MySQL с PHP. Мне нужна помощь в создании подготовленного оператора для извлечения столбцов.

Мне нужно получить информацию из разных столбцов. В настоящее время для тестового файла я используюсовершенно небезопасно Оператор SQL:

$qry = "SELECT * FROM mytable where userid='{$_GET['userid']}' AND category='{$_GET['category']}'ORDER BY id DESC"
$result = mysql_query($qry) or die(mysql_error()); 

Может ли кто-нибудь помочь мне создатьбезопасный MySQL оператор, использующий ввод из параметров URL (как выше), который подготовлен?

БОНУС: Предполагается, что подготовленные заявления также увеличивают скорость. Повысит ли это общую скорость, если я буду использовать подготовленное утверждение три или четыре раза на странице?

 outis26 мар. 2012 г., 04:05

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

Вы собираетесь использовать mysqli - что мне кажется лучшим решением - я настоятельно рекомендую скачать копиюcodesense_mysqli учебный класс.

Это'Это аккуратный маленький класс, который оборачивает и скрывает большую часть ненужной информации, которая накапливается при использовании raw mysqli, так что использование готовых операторов занимает всего одну или две дополнительные строки по сравнению со старым интерфейсом mysql / php

 chris18 авг. 2009 г., 19:31
Этот класс чрезвычайно прост в использовании. Интересно, почему что-то подобноет более популярным.

$qry = "SELECT * FROM mytable where userid='";
$qry.= mysql_real_escape_string($_GET['userid'])."' AND category='";
$qry.= mysql_real_escape_string($_GET['category'])."' ORDER BY id DESC";

Но чтобы использовать подготовленные операторы, лучше использовать общую библиотеку, напримерPDO

<!--?php
/* Execute a prepared statement by passing an array of values */
$sth = $dbh--->prepare('SELECT * FROM mytable where userid=? and category=? 
                      order by id DESC');
$sth->execute(array($_GET['userid'],$_GET['category']));
//Consider a while and $sth->fetch() to fetch rows one by one
$allRows = $sth->fetchAll(); 
?>

Или, используяMySQLi

<!--?php
$link = mysqli_connect("localhost", "my_user", "my_password", "world");

/* check connection */
if (mysqli_connect_errno()) {
    printf("Connect failed: %s\n", mysqli_connect_error());
    exit();
}

$category = $_GET['category'];
$userid = $_GET['userid'];

/* create a prepared statement */
if ($stmt = mysqli_prepare($link, 'SELECT col1, col2 FROM mytable where 
                      userid=? and category=? order by id DESC')) {
    /* bind parameters for markers */
    /* Assumes userid is integer and category is string */
    mysqli_stmt_bind_param($stmt, "is", $userid, $category);  
    /* execute query */
    mysqli_stmt_execute($stmt);
    /* bind result variables */
    mysqli_stmt_bind_result($stmt, $col1, $col2);
    /* fetch value */
    mysqli_stmt_fetch($stmt);
    /* Alternative, use a while:
    while (mysqli_stmt_fetch($stmt)) {
        // use $col1 and $col2 
    }
    */
    /* use $col1 and $col2 */
    echo "COL1: $col1 COL2: $col2\n";
    /* close statement */
    mysqli_stmt_close($stmt);
}

/* close connection */
mysqli_close($link);
?-->
Решение Вопроса

Вот пример использования mysqli (объектный синтаксис - при желании его довольно легко перевести в синтаксис функции):

$db = new mysqli("host","user","pw","database");
$stmt = $db->prepare("SELECT * FROM mytable where userid=? AND category=? ORDER BY id DESC");
$stmt->bind_param('ii', intval($_GET['userid']), intval($_GET['category']));
$stmt->execute();

$stmt->store_result();
$stmt->bind_result($column1, $column2, $column3);

while($stmt->fetch())
{
    echo "col1=$column1, col2=$column2, col3=$column3 \n";
}

$stmt->close();

Кроме того, если вы хотите получить простой способ получения ассоциативных массивов (для использования с SELECT *) вместо указания точно, к каким переменным привязываться, здесь 'Удобная функция:

function stmt_bind_assoc (&$stmt, &$out) {
    $data = mysqli_stmt_result_metadata($stmt);
    $fields = array();
    $out = array();

    $fields[0] = $stmt;
    $count = 1;

    while($field = mysqli_fetch_field($data)) {
        $fields[$count] = &$out[$field->name];
        $count++;
    }
    call_user_func_array(mysqli_stmt_bind_result, $fields);
}

Чтобы использовать его, просто вызовите его вместо вызова bind_result:

$stmt->store_result();

$resultrow = array();
stmt_bind_assoc($stmt, $resultrow);

while($stmt->fetch())
{
    print_r($resultrow);
}
 Krishnadas PC21 окт. 2013 г., 14:05
Очень хороший пример .. спасибо
 chris18 авг. 2009 г., 01:20
Благодарю. Как я могу отобразить результаты этого заявления? Обычно я использую mysql_fetch_row, это работает здесь?
 Amber18 авг. 2009 г., 01:32
Также обратите внимание, что мой пример предполагает, что оба ваших параметра являются целыми числами - если ониструны, тыМне нужно изменить аргументы на bind_param соответственно.
 Amber18 авг. 2009 г., 01:26
Обновлено, чтобы добавить пример того, как получить результаты.

Довольно поздно, но это может кому-то помочь

/**
* Execute query method. Executes a user defined query
*
* @param string $query the query statement
* @param array(Indexed) $col_vars the column variables to replace parameters. The count value should equal the number of supplied parameters
*
* Note: Use parameters in the query then supply the respective replacement variables in the second method parameter. e.g. 'SELECT COUNT(*) as count FROM foo WHERE bar = ?'
*
* @return array
*/
function read_sql($query, $col_vars=null) {
    $conn = new mysqli('hostname', 'username', 'user_pass', 'dbname');
    $types = $variables = array();
    if (isset($col_vars)) {
        for ($x=0; $x<count($col_vars); $x++)="" {="" switch="" (gettype($col_vars[$x]))="" case="" 'integer':="" array_push($types,="" 'i');="" break;="" 'double':="" 'd');="" default:="" 's');="" }="" array_push($variables,="" $col_vars[$x]);="" $types="implode(''," $types);="" $sql="$conn-">prepare($query);
        $sql -> bind_param($types, ...$variables);
        $sql -> execute();
        $results = $sql -> get_result();
        $sql -> close();
    }else {
        $results = $conn->query($query) or die('Error: '.$conn->error);
    }
    if ($results -> num_rows > 0) {
        while ($row = $results->fetch_assoc()) {
            $result[] = $row;
        }
        return $result;
    }else {
        return null;
    }
}
</count($col_vars);>

Затем вы можете вызвать функцию следующим образом:

read_sql('SELECT * FROM mytable where userid = ? AND category = ? ORDER BY id DESC', array($_GET['userid'], $_GET['category']));

рфейсом Mysql / PHP. Вы'понадобитсяPDO или жеMySQLi, Но если вы просто хотите заменить заполнители,проверьте этот комментарий на php 's mysql_query страница справочника.

вляется широко обсуждаемой проблемой. Вот несколько мест, где вы можете получить несколько полезных советов:

http://webmaster-forums.code-head.com/showthread.php?t=939http://www.sitepoint.com/article/php-security-blunders/http://dev.mysql.com/tech-resources/articles/guide-to-php-security.htmlhttp://www.scribd.com/doc/17638718/Module-11-PHP-MySQL-Database-Security-16

Два наиболее важных пункта, на мой взгляд:

SQL-инъекция: Обязательно экранируйте все переменные вашего запроса с помощью PHPmysql_real_escape_string() функция (или что-то подобное).Проверка входных данных: Никогда не доверяй пользователювход. Увидетьэтот для учебника о том, как правильно санировать и проверять ваши входные данные.
 chris18 авг. 2009 г., 19:32
@James: ссылка для проверки ввода очень полезна. Спасибо!
 Cheekysoft18 авг. 2009 г., 10:50
mysql_real_escape_string не велика и должна использоваться только в строковом контексте. Никогда не используйте его, чтобы попытаться защитить нестроковое поле. Пожалуйста, пожалуйста, используйте связанные параметры в предпочтении к этому списку фильтрации строк черного списка.

РНРext/mysql не поддерживает параметризованные операторы SQL.Параметры запросов считаются более надежными для защиты от проблем с внедрением SQL.mysql_real_escape_string() также может быть эффективным, если вы используете его правильно, но этоБолее подробный код.В некоторых версиях международные наборы символов имеют регистры символов, которые не экранированы должным образом, оставляя тонкие уязвимости. Использование параметров запроса позволяет избежать этих случаев.

Следует также отметить, что вам все равно следует проявлять осторожность при внедрении SQL, даже если вы используете параметры запроса, поскольку параметры заменяют только литеральные значения в запросах SQL. Если вы создаете запросы SQL динамически и используете переменные PHP для имени таблицы, имени столбца или любой другой части синтаксиса SQL, ни параметры запроса, ниmysql_real_escape_string() помочь в этом случае. Например:

$query = "SELECT * FROM $the_table ORDER BY $some_column"; 

Что касается производительности:

Повышение производительности достигается при многократном выполнении подготовленного запроса с различными значениями параметров. Вы избегаете накладных расходов при разборе и подготовке запроса. Но как часто вам нужно выполнять один и тот же SQL-запрос много раз в одном и том же PHP-запросе?Даже если вы можете воспользоваться этим преимуществом в производительности, это, как правило, лишь небольшое улучшение по сравнению со многими другими вещами, которые вы могли бы сделать для повышения производительности, такими как эффективное использование кэширования кода операции или данных.

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

SELECT * FROM mytable WHERE textfield LIKE ?
 chris19 авг. 2009 г., 00:00
отличный ответ. Как бы вы предложили мне справиться с таким заявлением, как "SELECT * ИЗ mytable WHERE текстовое поле, как? ", Должен ли я использовать подготовленное заявление или что-то еще?
 Bill Karwin19 авг. 2009 г., 00:15
В этом конкретном случае вы должны интерполировать шаблон в строку запроса: ""SELECT * FROM mytable WHERE textfield LIKE 'word%', Таким образом, оптимизатор может сказать, что он может использовать индекс (конечно, если ваш шаблон начинается с подстановочного знака, индекс в любом случае не дает никакой выгоды).

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