Почему я не должен использовать функции mysql * в PHP?

каковы технические причины, по которым не следует использовать mysql_* функции? (например,mysql_query(), mysql_connect() или mysql_real_escape_string())?

Почему я должен использовать что-то другое, даже если они работают на моем сайте?

если они не работают на моем сайте, Почему я получаю ошибки типа

предупреждение: mysql_connect (): нет такого файла или каталога

14 ответов


расширение MySQL:

  • не находится в стадии активного развития
  • Is официально устаревший по состоянию на PHP 5.5 (выпущен в июне 2013).
  • была удалены полностью начиная с PHP 7.0 (выпущен в декабре 2015 года)
    • это означает, что с 31 декабря 2018 он не будет существовать ни в одной поддерживаемой версии PHP. В настоящее время он получает только безопасность новинки.
  • отсутствует интерфейс OO
  • не поддерживает:
    • неблокирующие асинхронные запросы
    • подготовленные заявления или параметризованных запросов
    • хранимых процедур
    • Несколько Инструкций
    • сделки
    • "новый" метод аутентификации пароля (по умолчанию в MySQL 5.6; требуется в 5.7)
    • все функциональные возможности в MySQL 5.1

поскольку он устарел, использование его делает ваш код менее будущим доказательством.

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

посмотреть сравнение расширений SQL.


PHP предлагает три различных API для подключения к MySQL. Это mysql(удалено с PHP 7),mysqli и PDO расширения.

на mysql_* функции раньше были очень популярны, но их использование больше не поощряется. Команда документации обсуждает ситуацию с безопасностью базы данных, и обучение пользователей отходить от обычно используемого расширения ext/mysql является частью этого (проверка на PHP.internals: осуждение ext/mysql).

и более поздняя команда разработчиков PHP приняла решение создать E_DEPRECATED ошибки при подключении пользователей к MySQL, будь то через mysql_connect(), mysql_pconnect() или неявная функциональность подключения, встроенная в ext/mysql.

ext/mysql был официально устарел с PHP 5.5 и был удалено с PHP 7.

видите красную коробку?

когда вы идете на любой mysql_* страница руководства по функциям, вы видите Красное поле, объясняя, что его больше не следует использовать.

почему


двигался от ext/mysql не только о безопасности, но и о том, чтобы иметь доступ ко всем функциям базы данных MySQL.

ext/mysql был построен в в MySQL 3.23 и только получил очень мало дополнений с тех пор, в основном сохраняя совместимость с этой старой версией, что делает код немного сложнее поддерживать. Отсутствующие функции, которые не поддерживаются ext/mysql включают в себя: (из руководства PHP).

причина не использовать mysql_* функции:

  • не под активным развитием
  • удалено с PHP 7
  • отсутствует интерфейс OO
  • не поддерживает неблокирующие асинхронные запросы
  • не поддерживает подготовленные выражения или параметризованные запросы
  • не поддерживает хранимые процедуры
  • не поддерживает несколько операторов
  • не поддерживает сделки
  • не поддерживает все функциональные возможности в MySQL 5.1

выше привел цитату из ответа

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

посмотреть сравнение расширений SQL.


подавления предупреждения

в то время как код преобразуется в MySQLi/PDO, E_DEPRECATED ошибки можно подавить, установив error_reporting на на PHP.ini исключить E_DEPRECATED:

error_reporting = E_ALL ^ E_DEPRECATED

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

статьи PDO против MySQLi: что вы должны использовать? by Деян Марьянович поможет вам выбрать.

и лучший способ-это PDO и я сейчас пишу просто PDO руководство.


простой и короткий PDO учебник


Q. первый вопрос в моем уме был: что такое "PDO"?

А."PDO-PHP объекты данных - это уровень доступа к базе данных, обеспечивающий единый метод доступа к нескольким базам данных."

alt text


подключение к MySQL

с mysql_* функция или мы можем сказать это по-старому (устарел в PHP 5.5 и выше)

$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);

с PDO: все, что вам нужно сделать, это создать новый PDO и/или MySQLi не полностью безопасный. Проверьте ответ являются ли подготовленные PDO операторы достаточными для предотвращения SQL-инъекции? by ircmaxell. Кроме того, я цитирую часть его ответа:

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES GBK');
$stmt = $pdo->prepare("SELECT * FROM test WHERE name = ? LIMIT 1");
$stmt->execute(array(chr(0xbf) . chr(0x27) . " OR 1=1 /*"));

во-первых, давайте начнем со стандартного комментария, который мы даем всем:

пожалуйста, не используйте mysql_* функции в новом коде. Они больше не поддерживаются и официально осуждаемый. Вижу Красное поле? Узнайте о подготовленные заявления вместо этого и используйте PDO или MySQLi - в этой статье поможет вам решить, какой. Если вы выбираете PDO,вот хороший учебник.

давайте рассмотрим это предложение за предложением и объясним:

  • они больше не поддерживаются и официально осуждается

    это означает, что сообщество PHP постепенно теряет поддержку этих очень старых функций. Скорее всего, они не будут существовать в будущей (последней) версии PHP! Дальнейшее использование этих функций может нарушиться код в (не) далеком будущем.

    новый! - ext / mysql теперь официально устарел с PHP 5.5!

    новее! ext / mysql был удален в PHP 7.

  • вместо этого вы должны узнать о подготовленных заявлениях


простота использования

аналитические и синтетические причины уже упоминались. Для новичков есть более значительный стимул прекратить использование датированных функций mysql_.

современные API базы данных просто легче использовать.

это в основном связанные параметры что может упростить код. И с отличные учебники (как показано выше) переход к PDO это не слишком трудно.

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

эквивалентные функции pdo_ * вместо mysql_*

используя pdo_mysql.в PHP> вы можете переключиться со старых функций mysql_ с помощью минимальными усилиями. Он добавляет pdo_ функциональные обертки, которые заменяют их mysql_ сверстники.

  1. просто include_once("pdo_mysql.php"); в каждом скрипте вызова, который должен взаимодействовать с базой данных.

  2. удалить mysql_ префикс функция везде и заменить pdo_.

    • mysql_connect() будет pdo_connect()
    • mysql_query() становится pdo_query()
    • mysql_num_rows() становится pdo_num_rows()
    • mysql_insert_id() становится pdo_insert_id()
    • mysql_fetch_array() будет pdo_fetch_array()
    • mysql_fetch_assoc() становится pdo_fetch_assoc()
    • mysql_real_escape_string() становится pdo_real_escape_string()
    • и так далее...

  3. ваш код будет работать одинаково и по-прежнему в основном выглядеть одинаково:

    include_once("pdo_mysql.php"); 
    
    pdo_connect("localhost", "usrABC", "pw1234567");
    pdo_select_db("test");
    
    $result = pdo_query("SELECT title, html FROM pages");  
    
    while ($row = pdo_fetch_assoc($result)) {
        print "$row[title] - $row[html]";
    }
    

Et вот.
Ваш код используя PDO.
Теперь пришло время на самом деле использовать его.

связанные параметры могут быть просты в использовании

вам просто нужен менее громоздкий API.

pdo_query() добавляет очень легкую поддержку связанных параметров. Преобразование старого кода просто:

переместить переменные из SQL строка.

  • добавьте их в качестве параметров функции с разделителями-запятыми в pdo_query().
  • место вопросительные знаки ? как заполнители, где переменные были раньше.
  • избавиться ' одинарные кавычки, которые ранее заключали строковые значения / переменные.

преимущество становится более очевидным для более длинного кода.

часто строковые переменные не просто интерполируются в SQL, но объединяются с экранированием звонки между ними.

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title='" . pdo_real_escape_string($title) . "' OR id='".
   pdo_real_escape_string($title) . "' AND user <> '" .
   pdo_real_escape_string($root) . "' ORDER BY date")

с ? заполнители применяются, вам не нужно беспокоиться об этом:

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title=? OR id=? AND user<>? ORDER BY date", $title, $id, $root)

помните, что pdo_ * все еще позволяет или.
Просто не избежать переменной и свяжите его в том же запросе.

  • функция заполнителя обеспечивается реальным PDO за ним.
  • таким образом, также разрешено :named списки заполнитель позже.

что еще более важно, вы можете безопасно передавать переменные $_REQUEST[] за любым запросом. При подаче <form> поля соответствуют структуре базы данных точно, это еще короче:

pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);

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

исправить или удалить любой oldschool sanitize() функция

после преобразования все mysql_ звонки pdo_query с привязанными параметрами удалите все избыточные pdo_real_escape_string звонки.

в частности, вы должны исправить любые sanitize или clean или filterThis или clean_data функции как рекламируется от уроков в той или иной форме:

function sanitize($str) {
   return trim(strip_tags(htmlentities(pdo_real_escape_string($str))));
}

наиболее вопиющей ошибкой здесь является отсутствие документации. Что еще более важно, порядок фильтрации был в точности неправильным порядок.

  • правильный порядок был бы: deprecatedly stripslashes как внутренний зов, то trim, потом strip_tags, htmlentities для вывода контекста, и только, наконец,_escape_string как его приложение должно непосредственно предшествовать SQL intersparsing.

  • но как первый шаг просто избавьтесь от _real_escape_string звонок.

  • вы можете держать остаток


на mysql_ функции:

  1. устарели - они больше не поддерживаются
  2. не позволяйте вам легко переходить на другой сервер базы данных
  3. не поддерживают подготовленные заявления, следовательно
  4. поощряйте программистов использовать конкатенацию для создания запросов, что приводит к уязвимостям SQL-инъекций

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

  • неблокирующие асинхронные запросы
  • хранимые процедуры, возвращающие несколько наборов результатов
  • шифрование (SSL)
  • сжатие

если они вам нужны - это, без сомнения, технические причины для перехода от расширения mysql к чему-то более стильному и современному.

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

  • дальнейшее использование этих функций с современными версиями PHP вызовет уведомления об устаревшем уровне. Их просто можно отключить.
  • в отдаленном будущем они могут быть удалены из PHP по умолчанию строить. Не так уж много, поскольку mydsql ext будет перенесен в PECL, и каждый хостер будет рад скомпилировать PHP с ним, так как они не хотят терять клиентов, чьи сайты работали десятилетиями.
  • сильное сопротивление от сообщества Stackoverflow. Каждый раз, когда вы упоминаете эти честные функции, вам говорят, что они находятся под строгим табу.
  • будучи средним пользователем PHP, скорее всего, ваша идея использования этих функций подвержена ошибкам и ошибочна. Просто из-за всего ... эти многочисленные учебники и руководства, которые учат вас неправильный путь. Не сами функции - я должен это подчеркнуть , - а то, как они используются.

этот последний вопрос является проблемой.
Но, на мой взгляд, предложенное решение не лучше.
Мне кажется слишком идеалистична мечта, что все эти пользователи PHP узнают, как правильно обрабатывать SQL-запросы сразу. Скорее всего, они просто изменят mysql_* на mysqli_* механически, оставляя подход таким же. Тем более, что mysqli делает подготовленные заявления невероятно болезненными и хлопотными.
Не говоря уже о том, что уроженца подготовленные заявления недостаточно, чтобы защитить из SQL-инъекций, и ни mysqli, ни PDO не предлагают решения.

Итак, вместо того, чтобы бороться с этим честным расширением, я бы предпочел бороться с неправильными практиками и обучать людей правильным путям.

также, есть некоторые ложные или несущественные причины, такие как

  • не поддерживает хранимые процедуры (мы использовали mysql_query("CALL my_proc"); для возрастов)
  • не поддерживает транзакции (так же, как и выше)
  • не поддерживает несколько операторов (кому они нужны?)
  • не под активным развитием (так что? влияет ли это на вы каким-либо практическим образом?)
  • нет ОО интерфейс (создать-это вопрос нескольких часов)
  • не поддерживает подготовленные операторы или параметризованные запросы

последний интересный момент. Хотя mysql ext не поддерживает уроженца подготовленные заявления, они не требуются для безопасности. Мы можем легко подделать подготовленные заявления, используя заполнители с ручной обработкой (так же, как PDO):

function paraQuery()
{
    $args  = func_get_args();
    $query = array_shift($args);
    $query = str_replace("%s","'%s'",$query); 

    foreach ($args as $key => $val)
    {
        $args[$key] = mysql_real_escape_string($val);
    }

    $query  = vsprintf($query, $args);
    $result = mysql_query($query);
    if (!$result)
    {
        throw new Exception(mysql_error()." [$query]");
    }
    return $result;
}

$query  = "SELECT * FROM table where a=%s AND b LIKE %s LIMIT %d";
$result = paraQuery($query, $a, "%$b%", $limit);

вуаля!, все параметризовано и безопасно.

но хорошо, если вам не нравится красный ящик в руководстве, возникает проблема выбора: mysqli или PDO?

Ну, ответ будет следующим:

  • если вы понимаете необходимость использования уровень абстракции базы данных и ищет API для его создания,mysqli - очень хороший выбор, так как он действительно поддерживает многие функции mysql.
  • если, как и подавляющее большинство людей PHP, вы используете raw API-вызовы прямо в коде приложения (что по существу является неправильной практикой) -PDO-единственный выбор, поскольку это расширение притворяется не просто API, а скорее полу-даль, все еще неполный, но предлагает много важных функций, с двумя из них делает PDO критически отличающимся от mysqli:

    • в отличие от mysqli, PDO может связывать заполнители по стоимости, что делает динамически построенные запросы возможными без нескольких экранов довольно грязного кода.
    • в отличие от mysqli, PDO может всегда возвращайте результат запроса в простом обычном массиве, в то время как mysqli может сделать это только на установках mysqlnd.

Итак, если вы средний пользователь PHP и хотите сэкономить массу головных болей при использовании собственных подготовленных операторов, PDO - снова - единственный выбор.
Однако PDO не является серебряной пулей и имеет свои трудности.
Итак, я написал решения для всех общих подводных камней и сложных случаев в тег PDO wiki

тем не менее, все говорят о расширении всегда отсутствует 2 важных факта о Mysqli и PDO:

  1. подготовленное заявление - это не серебряная пуля. Существуют динамические идентификаторы, которые нельзя связать с помощью подготовленных операторов. Существуют динамические запросы с неизвестным количеством параметров, что затрудняет построение запросов задача.

  2. ни функции mysqli_*, ни PDO не должны были появиться в коде приложения.
    Должен быть абстракции между ними и кодом приложения,который будет выполнять всю грязную работу привязки, цикла, обработки ошибок и т. д. внутри, делая код приложения сухим и чистым. Особенно для сложных случаев, таких как динамическое построение запросов.

Итак, просто переключение на PDO или mysqli недостаточно. Вместо вызова необработанных функций API в коде необходимо использовать ORM, или построитель запросов, или любой класс абстракции базы данных.
И наоборот - если у вас есть слой абстракции между кодом приложения и API для MySQL на самом деле не имеет значения, какой двигатель используется. вы можете использовать mysql ext, пока он не устареет, а затем легко переписать свой класс абстракции на другой движок,имея весь код приложения нетронутый.

вот несколько примеров, основанных на my класс safemysql чтобы показать, каким должен быть такой класс абстракции:

$city_ids = array(1,2,3);
$cities   = $db->getCol("SELECT name FROM cities WHERE is IN(?a)", $city_ids);

сравните эту одну строку с количество кода вам понадобится с PDO.
Тогда сравните с сумасшедшее количество кода вам понадобится с необработанными mysqli подготовленными заявлениями. Обратите внимание, что обработка ошибок, профилирование, ведение журнала запросов уже встроены и бегущий.

$insert = array('name' => 'John', 'surname' => "O'Hara");
$db->query("INSERT INTO users SET ?u", $insert);

сравните его с обычными вставками PDO, когда каждое имя поля повторяется шесть - десять раз-во всех этих многочисленных именованных заполнителях, привязках и определениях запросов.

еще пример:

$data = $db->getAll("SELECT * FROM goods ORDER BY ?n", $_GET['order']);

вы вряд ли можете найти пример для PDO для обработки такого практического случая.
И это будет слишком многословно и, скорее всего, небезопасно.

Итак, еще раз - это не просто драйвер raw должен быть вашей заботой, но абстракцией класс, полезный не только для глупых примеров из руководства для начинающих, но и для решения любых реальных проблем.


есть много причин, но, возможно, самая важная заключается в том, что эти функции поощряют небезопасные методы программирования, потому что они не поддерживают подготовленные заявления. Подготовленные инструкции помогают предотвратить атаки SQL-инъекций.

при использовании mysql_* функции, вы должны помнить, чтобы запустить пользовательские параметры с помощью mysql_real_escape_string(). Если вы забыли только в одном месте или вам удалось избежать только части ввода, ваша база данных может быть подвержена атака.

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


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

в качестве примера кто-то может использовать "enhzflep); drop table users" в качестве имени пользователя. Старые функции позволят выполнять несколько операторов на запрос, поэтому что-то вроде этого неприятного Баггера может удалить всю таблицу.

Если использовать PDO mysqli, имя пользователя будет в конечном итоге "enhzflep); drop table users".

см.bobby-tables.com.


этот ответ написан, чтобы показать, насколько тривиально обходить плохо написанный код проверки пользователя PHP, как (и с помощью чего) эти атаки работают и как заменить старые функции MySQL безопасным подготовленным заявлением-и в основном, почему пользователи StackOverflow (вероятно, с большим количеством rep) лают на новых пользователей, задавая вопросы, чтобы улучшить свой код.

во-первых, пожалуйста, не стесняйтесь создавать эту тестовую базу данных mysql (я назвал мой prep):

mysql> create table users(
    -> id int(2) primary key auto_increment,
    -> userid tinytext,
    -> pass tinytext);
Query OK, 0 rows affected (0.05 sec)

mysql> insert into users values(null, 'Fluffeh', 'mypass');
Query OK, 1 row affected (0.04 sec)

mysql> create user 'prepared'@'localhost' identified by 'example';
Query OK, 0 rows affected (0.01 sec)

mysql> grant all privileges on prep.* to 'prepared'@'localhost' with grant option;
Query OK, 0 rows affected (0.00 sec)

после этого мы можем перейти к нашему PHP-коду.

предположим, что следующий скрипт-это процесс проверки администратора на веб-сайте (упрощенный, но работающий, если вы копируете и используете его для тестирования):

<?php 

    if(!empty($_POST['user']))
    {
        $user=$_POST['user'];
    }   
    else
    {
        $user='bob';
    }
    if(!empty($_POST['pass']))
    {
        $pass=$_POST['pass'];
    }
    else
    {
        $pass='bob';
    }

    $database='prep';
    $link=mysql_connect('localhost', 'prepared', 'example');
    mysql_select_db($database) or die( "Unable to select database");

    $sql="select id, userid, pass from users where userid='$user' and pass='$pass'";
    //echo $sql."<br><br>";
    $result=mysql_query($sql);
    $isAdmin=false;
    while ($row = mysql_fetch_assoc($result)) {
        echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>";
        $isAdmin=true;
        // We have correctly matched the Username and Password
        // Lets give this person full access
    }
    if($isAdmin)
    {
        echo "The check passed. We have a verified admin!<br>";
    }
    else
    {
        echo "You could not be verified. Please try again...<br>";
    }
    mysql_close($link);

?>

<form name="exploited" method='post'>
    User: <input type='text' name='user'><br>
    Pass: <input type='text' name='pass'><br>
    <input type='submit'>
</form>

на первый взгляд кажется вполне законным.

пользователь должен ввести логин и пароль, верно?

блестящий, не входите в следующее:

user: bob
pass: somePass

и представить он.

вывод выглядит следующим образом:

You could not be verified. Please try again...

супер! Работая как ожидалось, теперь давайте попробуем фактическое имя пользователя и пароль:

user: Fluffeh
pass: mypass

потрясающе! Привет-пятерки все кругом, код правильно проверил админ. Это прекрасно!

Ну, не совсем. Допустим, пользователь умный маленький человек. Допустим, человек-это я.

введите следующее:

user: bob
pass: n' or 1=1 or 'm=m

и выход есть:

The check passed. We have a verified admin!

поздравляю, вы только что позволили мне войти в ваш супер-защищенный раздел admins only со мной, введя ложное имя пользователя и ложный пароль. Серьезно, если вы мне не верите, создайте базу данных с кодом, который я предоставил, и запустите этот PHP - код, который на первый взгляд действительно, кажется, проверяет имя пользователя и пароль довольно красиво.

Итак, в ответ, вот почему на вас кричат.

Итак, давайте посмотрим, что пошло не так, и почему Я только что вошел в твою супер-админ-только-летучую-пещеру. Я сделал предположение и предположил, что вы не были осторожны со своими вводами и просто передали их в базу данных напрямую. Я построил ввод таким образом, чтобы изменить запрос, который вы фактически выполняли. Итак, что это должно было быть, и чем это закончилось?

select id, userid, pass from users where userid='$user' and pass='$pass'

это запрос, но когда мы заменяем переменные фактическими входными данными, которые мы использовали, мы получаем следующее:

select id, userid, pass from users where userid='bob' and pass='n' or 1=1 or 'm=m'

посмотреть как я построил свой "пароль", чтобы сначала закрыть одну цитату вокруг пароля, а затем ввести совершенно новое сравнение? Затем, просто для безопасности, я добавил еще одну "строку", чтобы одиночная кавычка была закрыта, как ожидалось в коде, который у нас изначально был.

однако, это не о людях, кричащих на вас сейчас, это о том, чтобы показать вам, как сделать ваш код более безопасным.

Итак, что пошло не так, и как мы можем это исправить?

этот это классическая атака SQL-инъекций. Один из самых простых. В масштабе векторов атаки это малыш, атакующий танк-и побеждающий.

Итак, как мы защищаем ваш священный раздел администратора и делаем его приятным и безопасным? Первое, что нужно сделать, это прекратить использовать эти действительно старые и устаревшие mysql_* функции. Я знаю, вы следовали учебнику, который вы нашли в интернете, и он работает, но он старый, он устарел, и в течение нескольких минут я только что сломал прошлое даже не вспотев.

теперь у вас есть лучшие варианты использования mysqli_ или PDO. Я лично большой поклонник PDO, поэтому я буду использовать PDO в остальной части этого ответа. Есть " за " и "против", но лично я считаю, что " за "намного перевешивает "против". Он переносим через несколько ядер баз данных - используете ли вы MySQL или Oracle или просто что - нибудь кровавое- просто изменив строку подключения, у него есть все причудливые функции, которые мы хотим использовать, и это красиво и чисто. Я люблю чистоту.

теперь давайте снова посмотрим на этот код, на этот раз написанный с помощью объекта PDO:

<?php 

    if(!empty($_POST['user']))
    {
        $user=$_POST['user'];
    }   
    else
    {
        $user='bob';
    }
    if(!empty($_POST['pass']))
    {
        $pass=$_POST['pass'];
    }
    else
    {
        $pass='bob';
    }
    $isAdmin=false;

    $database='prep';
    $pdo=new PDO ('mysql:host=localhost;dbname=prep', 'prepared', 'example');
    $sql="select id, userid, pass from users where userid=:user and pass=:password";
    $myPDO = $pdo->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
    if($myPDO->execute(array(':user' => $user, ':password' => $pass)))
    {
        while($row=$myPDO->fetch(PDO::FETCH_ASSOC))
        {
            echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>";
            $isAdmin=true;
            // We have correctly matched the Username and Password
            // Lets give this person full access
        }
    }

    if($isAdmin)
    {
        echo "The check passed. We have a verified admin!<br>";
    }
    else
    {
        echo "You could not be verified. Please try again...<br>";
    }

?>

<form name="exploited" method='post'>
    User: <input type='text' name='user'><br>
    Pass: <input type='text' name='pass'><br>
    <input type='submit'>
</form>

основные различия в том, что нет больше mysql_* функции. Все это делается через объект PDO, во-вторых, он использует подготовленный оператор. Теперь, что такое заявление prepred спросите вы? Это способ сообщить базе данных перед запуском запроса, какой запрос мы собираемся запустить. В этом случай, мы говорим базе данных: "Привет, я собираюсь запустить оператор select, желающий id, userid и передать от пользователей таблицы, где userid является переменной, а pass также переменной.".

затем в инструкции execute мы передаем базе данных массив со всеми переменными, которые она теперь ожидает.

результаты фантастические. Давайте попробуем эти комбинации имени пользователя и пароля еще раз:

user: bob
pass: somePass

пользователь не проверил. Потрясающий.

как насчет:

user: Fluffeh
pass: mypass

О, я просто немного взволновался, это сработало: чек прошел. У нас есть проверенный админ!

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

user: bob
pass: n' or 1=1 or 'm=m

в этот раз, мы получаем следующее:

You could not be verified. Please try again...

вот почему на вас кричат при публикации вопросов-это потому, что люди могут видеть, что ваш код можно обойти wihout даже пытается. Пожалуйста, используйте этот вопрос и ответ, чтобы улучшить свой код, сделать его более безопасным и использовать текущие функции.

наконец, это не значит, что это идеальный код. Есть еще много вещей, которые вы могли бы сделать, чтобы улучшить его, использовать хэшированные пароли, например, убедитесь, что при хранении сенсорной информации в базе данных вы не храните ее в обычном тексте, имеете несколько уровней проверки - но на самом деле, если вы просто измените свою старую инъекцию код к этому, вы будете хорошо на пути к написанию хорошего кода - и тот факт, что вы получили это далеко и все еще читаете, дает мне чувство надежды, что вы не только реализуете этот тип кода при написании ваших веб - сайтов и приложений, но что вы можете выйти и исследовать те другие вещи, которые я только что упомянул-и многое другое. Напишите лучший код, который вы можете, а не самый простой код, который едва функционирует.


расширение MySQL является самым старым из трех и было оригинальным способом, который разработчики использовали для связи с MySQL. Это расширение теперь устаревший в пользу другой два варианты из-за улучшений, сделанных в более новых выпусках PHP и MySQL.

  • MySQLi является "улучшенным" расширением для работы с базами данных MySQL. Он использует преимущества функций, которые доступны в более новые версии сервера MySQL, предоставляет как функционально-ориентированный, так и объектно-ориентированный интерфейс разработчику и делает несколько других изящных вещей.

  • PDO предлагает API, который объединяет большую часть функций, которые ранее были распространены по основным расширениям доступа к базе данных, т. е. MySQL, PostgreSQL, SQLite, MSSQL и т. д. Интерфейс предоставляет высокоуровневые объекты для работы программиста с подключениями к базе данных, запросами и результатом наборы и низкоуровневые драйверы выполняют связь и обработку ресурсов с сервером базы данных. В PDO много дискуссий и работы, и это считается подходящим методом работы с базами данных в современном профессиональном коде.


Я нахожу приведенные выше ответы очень длинными, поэтому подытожим:

расширение mysqli имеет ряд преимущества, ключевые улучшения над расширение mysql является:

  • объектно-ориентированный интерфейс
  • поддержка подготовленных заявлений
  • поддержка нескольких операторов
  • сопровождение сделок
  • расширенные возможности отладки
  • встроенный сервер поддержка

источник: обзор MySQLi


как объяснено в приведенных выше ответах, альтернативами mysql являются mysqli и PDO (объекты данных PHP).

  • API поддерживает подготовленные на стороне сервера заявления: поддерживается MYSQLi и PDO
  • API поддерживает подготовленные заявления на стороне клиента: поддерживается только PDO
  • API поддерживает хранимые процедуры: MySQLi и PDO
  • поддержка API Несколько операторов и вся функциональность MySQL 4.1+ - поддерживается MySQLi и в основном также PDO

оба MySQLi и PDO были введены в PHP 5.0, тогда как MySQL был введен до PHP 3.0. Следует отметить, что MySQL включен в PHP5.x хотя и устарел в более поздних версиях.


можно определить почти все mysql_* функции, использующие mysqli или PDO. Просто включите их поверх вашего старого приложения PHP, и он будет работать на PHP7. Мое решение здесь.

<?php

define('MYSQL_LINK', 'dbl');
$GLOBALS[MYSQL_LINK] = null;

function mysql_link($link=null) {
    return ($link === null) ? $GLOBALS[MYSQL_LINK] : $link;
}

function mysql_connect($host, $user, $pass) {
    $GLOBALS[MYSQL_LINK] = mysqli_connect($host, $user, $pass);
    return $GLOBALS[MYSQL_LINK];
}

function mysql_pconnect($host, $user, $pass) {
    return mysql_connect($host, $user, $pass);
}

function mysql_select_db($db, $link=null) {
    $link = mysql_link($link);
    return mysqli_select_db($link, $db);
}

function mysql_close($link=null) {
    $link = mysql_link($link);
    return mysqli_close($link);
}

function mysql_error($link=null) {
    $link = mysql_link($link);
    return mysqli_error($link);
}

function mysql_errno($link=null) {
    $link = mysql_link($link);
    return mysqli_errno($link);
}

function mysql_ping($link=null) {
    $link = mysql_link($link);
    return mysqli_ping($link);
}

function mysql_stat($link=null) {
    $link = mysql_link($link);
    return mysqli_stat($link);
}

function mysql_affected_rows($link=null) {
    $link = mysql_link($link);
    return mysqli_affected_rows($link);
}

function mysql_client_encoding($link=null) {
    $link = mysql_link($link);
    return mysqli_character_set_name($link);
}

function mysql_thread_id($link=null) {
    $link = mysql_link($link);
    return mysqli_thread_id($link);
}

function mysql_escape_string($string) {
    return mysql_real_escape_string($string);
}

function mysql_real_escape_string($string, $link=null) {
    $link = mysql_link($link);
    return mysqli_real_escape_string($link, $string);
}

function mysql_query($sql, $link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, $sql);
}

function mysql_unbuffered_query($sql, $link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, $sql, MYSQLI_USE_RESULT);
}

function mysql_set_charset($charset, $link=null){
    $link = mysql_link($link);
    return mysqli_set_charset($link, $charset);
}

function mysql_get_host_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_host_info($link);
}

function mysql_get_proto_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_proto_info($link);
}
function mysql_get_server_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_server_info($link);
}

function mysql_info($link=null) {
    $link = mysql_link($link);
    return mysqli_info($link);
}

function mysql_get_client_info() {
    $link = mysql_link();
    return mysqli_get_client_info($link);
}

function mysql_create_db($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "CREATE DATABASE `$db`");
}

function mysql_drop_db($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "DROP DATABASE `$db`");
}

function mysql_list_dbs($link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, "SHOW DATABASES");
}

function mysql_list_fields($db, $table, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    $table = str_replace('`', '', mysqli_real_escape_string($link, $table));
    return mysqli_query($link, "SHOW COLUMNS FROM `$db`.`$table`");
}

function mysql_list_tables($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "SHOW TABLES FROM `$db`");
}

function mysql_db_query($db, $sql, $link=null) {
    $link = mysql_link($link);
    mysqli_select_db($link, $db);
    return mysqli_query($link, $sql);
}

function mysql_fetch_row($qlink) {
    return mysqli_fetch_row($qlink);
}

function mysql_fetch_assoc($qlink) {
    return mysqli_fetch_assoc($qlink);
}

function mysql_fetch_array($qlink, $result=MYSQLI_BOTH) {
    return mysqli_fetch_array($qlink, $result);
}

function mysql_fetch_lengths($qlink) {
    return mysqli_fetch_lengths($qlink);
}

function mysql_insert_id($qlink) {
    return mysqli_insert_id($qlink);
}

function mysql_num_rows($qlink) {
    return mysqli_num_rows($qlink);
}

function mysql_num_fields($qlink) {
    return mysqli_num_fields($qlink);
}

function mysql_data_seek($qlink, $row) {
    return mysqli_data_seek($qlink, $row);
}

function mysql_field_seek($qlink, $offset) {
    return mysqli_field_seek($qlink, $offset);
}

function mysql_fetch_object($qlink, $class="stdClass", array $params=null) {
    return ($params === null)
        ? mysqli_fetch_object($qlink, $class)
        : mysqli_fetch_object($qlink, $class, $params);
}

function mysql_db_name($qlink, $row, $field='Database') {
    mysqli_data_seek($qlink, $row);
    $db = mysqli_fetch_assoc($qlink);
    return $db[$field];
}

function mysql_fetch_field($qlink, $offset=null) {
    if ($offset !== null)
        mysqli_field_seek($qlink, $offset);
    return mysqli_fetch_field($qlink);
}

function mysql_result($qlink, $offset, $field=0) {
    if ($offset !== null)
        mysqli_field_seek($qlink, $offset);
    $row = mysqli_fetch_array($qlink);
    return (!is_array($row) || !isset($row[$field]))
        ? false
        : $row[$field];
}

function mysql_field_len($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    return is_object($field) ? $field->length : false;
}

function mysql_field_name($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    if (!is_object($field))
        return false;
    return empty($field->orgname) ? $field->name : $field->orgname;
}

function mysql_field_table($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    if (!is_object($field))
        return false;
    return empty($field->orgtable) ? $field->table : $field->orgtable;
}

function mysql_field_type($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    return is_object($field) ? $field->type : false;
}

function mysql_free_result($qlink) {
    try {
        mysqli_free_result($qlink);
    } catch (Exception $e) {
        return false;
    }
    return true;
}

функции, которые так же похожи на это mysql_connect(), mysql_query() type-это предыдущая версия PHP i.e (PHP 4) функции и теперь не используются .

они заменены на mysqli_connect(), mysqli_query() аналогично в последнем PHP5.

это причина ошибки.


mysql_* функции были амортизированы (по состоянию на php 5.5) учитывая тот факт, что были разработаны лучшие функции и структуры кода. Тот факт, что функция была обесценена, означает, что больше не будет прилагаться усилий для ее улучшения с точки зрения производительности и безопасности, что означает, что меньше на будущее.

Если вам нужно больше причин:

  • mysql_* функции не поддерживают подготовленный заявления.
  • mysql_* функции не поддерживают привязку параметров.
  • mysql_* функции не имеют функциональности для объектно-ориентированного программирования.
  • далее по списку ...