Intereting Posts
Неустранимая ошибка: вызов неопределенного метода GuzzleHttp \ Client :: request () с помощью Guzzle 6 Преобразование double в строку в php Неустранимая ошибка при запуске потоков в команде symfony Получение каталога, метаданных или информации о схеме из базы данных MS Access при подключении к PHP Определить количество измерений в массиве PHP Как подключить несколько баз данных в инфраструктуре phalcon в то же время использовать как в классе модели не только один как аутентифицировать RESTful API в Laravel 5? Пытается получить случайное значение массива для эха в классе элемента введите ключ для сохранения значений textarea Переупорядочить XML-результаты на основе данных XML Фильтр и разбиение на страницы на CodeIgniter Как использовать Ajax с jQuery в Laravel 4? PHP date_default_timezone_set () PHP Получить имя текущего каталога Сайт, созданный в Codeigniter, загружает только домашнюю страницу

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

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

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

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

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

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

  • Не активно развивается
  • Официально устарел с PHP 5.5 (выпущен в июне 2013 года).
  • Был полностью удален с PHP 7.0 (выпущен в декабре 2015 года)
    • Это означает, что с 31 декабря 2018 года он не будет существовать ни в одной поддерживаемой версии PHP. В настоящее время он получает обновления для системы безопасности .
  • Отсутствует интерфейс OO
  • Не поддерживает:
    • Неблокирующие, асинхронные запросы
    • Подготовленные утверждения или параметризованные запросы
    • Хранимые процедуры
    • Несколько заявлений
    • операции
    • «Новый» метод проверки пароля (по умолчанию в MySQL 5.6, требуемый в 5.7)
    • Все функциональные возможности MySQL 5.1

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

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

См. Сравнение расширений SQL .

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

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

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

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

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

    NEW! – ext / mysql теперь официально устарел от PHP 5.5!

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

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

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

    Дополнительные сведения см. В разделе Как предотвратить SQL-инъекцию в PHP?

  • См. Красную рамку?

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

  • Используйте PDO или MySQLi

    Существуют лучшие, более надежные и хорошо построенные альтернативы, PDO – объект базы данных PHP , который предлагает полный подход ООП к взаимодействию с базой данных и MySQLi , что является улучшением в MySQL.

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

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

Современные API баз данных проще в использовании.

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

Однако переписывание большей базы кода требует времени. Raison d'être для этой промежуточной альтернативы:

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

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

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

  2. Удалите mysql_ функции 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]"; } 

И вуаля.
В вашем коде используется 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 позже :named списки закладок.

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

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

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

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

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

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

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

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

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

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

  • Возможно, вам придется сохранить остальную часть вашей функции sanitize() если ваша база данных и поток приложений ожидают строки, совместимые с HTML-контекстом. Добавьте комментарий, который в дальнейшем применяется только для HTML.

  • Обработка строк / значений делегируется PDO и параметризованным операторам.

  • Если было какое-либо упоминание о stripslashes() в вашей функции stripslashes() , это может указывать на более высокий уровень надзора.

    • Это было обычно для того, чтобы отменить урон (двойной выход) из устаревших magic_quotes . Который, однако, лучше всего фиксирован по центру , а не по строкам.

    • Используйте один из подходов к разворачиванию прав пользователя . Затем удалите stripslashes() в функции sanitize .

    Историческая заметка о magic_quotes. Эта функция по праву устарела. Однако это часто неправильно отображается как неисправная функция безопасности . Но magic_quotes – это такая же неудачная функция безопасности, как и теннисные мячи в качестве источника питания. Это просто не их цель.

    Первоначальная реализация в PHP2 / FI ввела его в явном виде, просто « кавычки будут автоматически экранированы, что упростит передачу данных формы непосредственно в запросы msql ». В частности, это было случайно безопасно использовать с mSQL , поскольку это поддерживало только ASCII.
    Затем PHP3 / Zend повторно включил magic_quotes для MySQL и неправильно документировал его. Но первоначально это была просто удобная функция , а не для обеспечения безопасности.

Как подготовленные заявления различаются

Когда вы вставляете строковые переменные в SQL-запросы, это не просто становится более сложным для вас. Это также посторонние усилия для MySQL, чтобы снова разделить код и данные.

SQL-инъекции просто состоят в том, что данные попадают в контекст кода . Сервер базы данных не может впоследствии определить, где PHP первоначально склеивал переменные между предложениями запроса.

С привязанными параметрами вы разделяете значения кода SQL и SQL-контекста в вашем PHP-коде. Но он не перетасовывается снова за кулисами (кроме PDO :: EMULATE_PREPARES). Ваша база данных получает неизменные команды SQL и значения переменных 1: 1.

Хотя в этом ответе подчеркивается, что вы должны заботиться о преимуществах чтения, связанных с удалением mysql_ . Иногда из-за этого видимого и технического разделения данных / кода иногда появляется преимущество в производительности (повторяющиеся INSERT с только разными значениями).

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

Использование гибридного PDO

Эти pdo_* обертки pdo_* создают удобный для кодирования API-интерфейс стоп-зазора. (Это в значительной степени то, что могло бы быть MYSQLI , если бы не MYSQLI сдвиг подписи функции). Они также демонстрируют реальный PDO в большинстве случаев.
Переписывание не обязательно останавливается при использовании новых имен функций pdo_. Вы можете по одному переходу каждый pdo_query () в простой вызов $ pdo-> prepare () -> execute ().

Лучше всего начать с упрощения снова. Например, общий результат:

 $result = pdo_query("SELECT * FROM tbl"); while ($row = pdo_fetch_assoc($result)) { 

Может быть заменен только итерацией foreach:

 foreach ($result as $row) { 

Или, еще лучше, прямой и полный поиск массива:

 $result->fetchAll(); 

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

Другие варианты

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

Просто переключение на pdo не совсем сократило его. pdo_query() также является интерфейсом.

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

Несмотря на то, что он относится к категории простейших вещей, которые могут быть возможными, это также очень экспериментальный код. Я только что написал это в выходные. There's a plethora of alternatives however. Just google for PHP database abstraction and browse a little. There always have been and will be lots of excellent libraries for such tasks.

If you want to simplify your database interaction further, mappers like Paris/Idiorm are worth a try. Just like nobody uses the bland DOM in JavaScript anymore, you don't have to babysit a raw database interface nowadays.

The mysql_ functions are:

  1. Out of date – they're not maintained anymore
  2. Don't allow you to move easily to another database backend
  3. Don't support prepared statements, hence
  4. Encourage programmers to use concatenation to build queries, leading to SQL injection vulnerabilities.

Speaking of technical reasons, there are only a few, extremely specific and rarely used. Most likely you will never ever use them in your life.
Maybe I am too ignorant, but I never had an opportunity to use them things like

  • non-blocking, asynchronous queries
  • stored procedures returning multiple resultsets
  • Encryption (SSL)
  • компрессия

If you need them – these are no doubt technical reasons to move away from mysql extension toward something more stylish and modern-looking.

Nevertheless, there are also some non-technical issues, which can make your experience a bit harder

  • further use of these functions with modern PHP versions will raise deprecated-level notices. They simply can be turned off.
  • in a distant future, they can be possibly removed from the default PHP build. Not a big deal too, as mydsql ext will be moved into PECL and every hoster will be happy to compile PHP with it, as they don't want to lose clients whose sites were working for decades.
  • strong resistance from Stackoverflow community. Еverytime you mention these honest functions, you being told that they are under strict taboo.
  • being an average PHP user, most likely your idea of using these functions is error-prone and wrong. Just because of all these numerous tutorials and manuals which teach you the wrong way. Not the functions themselves – I have to emphasize it – but the way they are used.

This latter issue is a problem.
But, in my opinion, the proposed solution is no better either.
It seems to me too idealistic a dream that all those PHP users will learn how to handle SQL queries properly at once. Most likely they would just change mysql_* to mysqli_* mechanically, leaving the approach the same . Especially because mysqli makes prepared statements usage incredible painful and troublesome.
Not to mention that native prepared statements aren't enough to protect from SQL injections, and neither mysqli nor PDO offers a solution.

So, instead of fighting this honest extension, I'd prefer to fight wrong practices and educate people in the right ways.

Also, there are some false or non-significant reasons, like

  • Doesn't support Stored Procedures (we were using mysql_query("CALL my_proc"); for ages)
  • Doesn't support Transactions (same as above)
  • Doesn't support Multiple Statements (who need them?)
  • Not under active development (so what? does it affect you in any practical way?)
  • Lacks an OO interface (to create one is a matter of several hours)
  • Doesn't support Prepared Statements or Parametrized Queries

The last one is an interesting point. Although mysql ext do not support native prepared statements, they aren't required for the safety. We can easily fake prepared statements using manually handled placeholders (just like PDO does):

 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); 

voila , everything is parameterized and safe.

But okay, if you don't like the red box in the manual, a problem of choice arises: mysqli or PDO?

Well, the answer would be as follows:

  • If you understand the necessity of using a database abstraction layer and looking for an API to create one, mysqli is a very good choice, as it indeed supports many mysql-specific features.
  • If, like vast majority of PHP folks, you are using raw API calls right in the application code (which is essentially wrong practice) – PDO is the only choice , as this extension pretends to be not just API but rather a semi-DAL, still incomplete but offers many important features, with two of them makes PDO critically distinguished from mysqli:

    • unlike mysqli, PDO can bind placeholders by value , which makes dynamically built queries feasible without several screens of quite messy code.
    • unlike mysqli, PDO can always return query result in a simple usual array, while mysqli can do it only on mysqlnd installations.

So, if you are an average PHP user and want to save yourself a ton of headaches when using native prepared statements, PDO – again – is the only choice.
However, PDO is not a silver bullet too and has its hardships.
So, I wrote solutions for all the common pitfalls and complex cases in the PDO tag wiki

Nevertheless, everyone talking about extensions always missing the 2 important facts about Mysqli and PDO:

  1. Prepared statement isn't a silver bullet . There are dynamical identifiers which cannot be bound using prepared statements. There are dynamical queries with an unknown number of parameters which makes query building a difficult task.

  2. Neither mysqli_* nor PDO functions should have appeared in the application code.
    There ought to be an abstraction layer between them and application code, which will do all the dirty job of binding, looping, error handling, etc. inside, making application code DRY and clean. Especially for the complex cases like dynamical query building.

So, just switching to PDO or mysqli is not enough. One has to use an ORM, or a query builder, or whatever database abstraction class instead of calling raw API functions in their code.
And contrary – if you have an abstraction layer between your application code and mysql API – it doesn't actually matter which engine is used. You can use mysql ext until it goes deprecated and then easily rewrite your abstraction class to another engine, having all the application code intact.

Here are some examples based on my safemysql class to show how such an abstraction class ought to be:

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

Compare this one single line with amount of code you will need with PDO .
Then compare with crazy amount of code you will need with raw Mysqli prepared statements. Note that error handling, profiling, query logging already built in and running.

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

Compare it with usual PDO inserts, when every single field name being repeated six to ten times – in all these numerous named placeholders, bindings, and query definitions.

Другой пример:

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

You can hardly find an example for PDO to handle such practical case.
And it will be too wordy and most likely unsafe.

So, once more – it is not just raw driver should be your concern but abstraction class, useful not only for silly examples from beginner's manual but to solve whatever real-life problems.

There are many reasons, but perhaps the most important one is that those functions encourage insecure programming practices because they do not support prepared statements. Prepared statements help prevent SQL injection attacks.

When using mysql_* functions, you have to remember to run user-supplied parameters through mysql_real_escape_string() . If you forget in just one place or if you happen to escape only part of the input, your database may be subject to attack.

Using prepared statements in PDO or mysqli will make it so that these sorts of programming errors are more difficult to make.

Because (amongst other reasons) it's much harder to ensure the input data is sanitized. If you use parametrized queries, as one does with PDO or mysqli you can entirely avoid the risk.

As an example, someone could use "enhzflep); drop table users" as a username. The old functions will allow executing multiple statements per query, so something like that nasty bugger can delete a whole table.

If one were to use PDO of mysqli, the user-name would end-up being "enhzflep); drop table users" .

See bobby-tables.com .

This answer is written to show just how trivial it is to bypass poorly written PHP user-validation code, how (and using what) these attacks work and how to replace the old MySQL functions with a secure prepared statement – and basically, why StackOverflow users (probably with a lot of rep) are barking at new users asking questions to improve their code.

First off, please feel free to create this test mysql database (I have called mine 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) 

With that done, we can move to our PHP code.

Lets assume the following script is the verification process for an admin on a website (simplified but working if you copy and use it for testing):

 <?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> 

Seems legit enough at first glance.

The user has to enter a login and password, right?

Brilliant, not enter in the following:

 user: bob pass: somePass 

and submit it.

The output is as follows:

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

Супер! Working as expected, now lets try the actual username and password:

 user: Fluffeh pass: mypass 

Удивительно! Hi-fives all round, the code correctly verified an admin. It's perfect!

Well, not really. Lets say the user is a clever little person. Lets say the person is me.

Enter in the following:

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

And the output is:

 The check passed. We have a verified admin! 

Congrats, you just allowed me to enter your super-protected admins only section with me entering a false username and a false password. Seriously, if you don't believe me, create the database with the code I provided, and run this PHP code – which at glance REALLY does seem to verify the username and password rather nicely.

So, in answer, THAT IS WHY YOU ARE BEING YELLED AT.

So, lets have a look at what went wrong, and why I just got into your super-admin-only-bat-cave. I took a guess and assumed that you weren't being careful with your inputs and simply passed them to the database directly. I constructed the input in a way tht would CHANGE the query that you were actually running. So, what was it supposed to be, and what did it end up being?

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

That's the query, but when we replace the variables with the actual inputs that we used, we get the following:

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

See how I constructed my "password" so that it would first close the single quote around the password, then introduce a completely new comparison? Then just for safety, I added another "string" so that the single quote would get closed as expected in the code we originally had.

However, this isn't about folks yelling at you now, this is about showing you how to make your code more secure.

Okay, so what went wrong, and how can we fix it?

This is a classic SQL injection attack. One of the simplest for that matter. On the scale of attack vectors, this is a toddler attacking a tank – and winning.

So, how do we protect your sacred admin section and make it nice and secure? The first thing to do will be to stop using those really old and deprecated mysql_* functions. I know, you followed a tutorial you found online and it works, but it's old, it's outdated and in the space of a few minutes, I have just broken past it without so much as breaking a sweat.

Now, you have the better options of using mysqli_ or PDO . I am personally a big fan of PDO, so I will be using PDO in the rest of this answer. There are pro's and con's, but personally I find that the pro's far outweigh the con's. It's portable across multiple database engines – whether you are using MySQL or Oracle or just about bloody anything – just by changing the connection string, it has all the fancy features we want to use and it is nice and clean. I like clean.

Now, lets have a look at that code again, this time written using a PDO object:

 <?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> 

The major differences are that there are no more mysql_* functions. It's all done via a PDO object, secondly, it is using a prepared statement. Now, what's a prepred statement you ask? It's a way to tell the database ahead of running a query, what the query is that we are going to run. In this case, we tell the database: "Hi, I am going to run a select statement wanting id, userid and pass from the table users where the userid is a variable and the pass is also a variable.".

Then, in the execute statement, we pass the database an array with all the variables that it now expects.

The results are fantastic. Lets try those username and password combinations from before again:

 user: bob pass: somePass 

User wasn't verified. Потрясающие.

Как насчет:

 user: Fluffeh pass: mypass 

Oh, I just got a little excited, it worked: The check passed. We have a verified admin!

Now, lets try the data that a clever chap would enter to try to get past our little verification system:

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

This time, we get the following:

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

This is why you are being yelled at when posting questions – it's because people can see that your code can be bypassed wihout even trying. Please, do use this question and answer to improve your code, to make it more secure and to use functions that are current.

Lastly, this isn't to say that this is PERFECT code. There are many more things that you could do to improve it, use hashed passwords for example, ensure that when you store sensetive information in the database, you don't store it in plain text, have multiple levels of verification – but really, if you just change your old injection prone code to this, you will be WELL along the way to writing good code – and the fact that you have gotten this far and are still reading gives me a sense of hope that you will not only implement this type of code when writing your websites and applications, but that you might go out and research those other things I just mentioned – and more. Write the best code you can, not the most basic code that barely functions.

The MySQL extension is the oldest of the three and was the original way that developers used to communicate with MySQL. This extension is now being deprecated in favor of the other two alternatives because of improvements made in newer releases of both PHP and MySQL.

  • MySQLi is the 'improved' extension for working with MySQL databases. It takes advantage of features that are available in newer versions of the MySQL server, exposes both a function-oriented and an object-oriented interface to the developer and a does few other nifty things.

  • PDO offers an API that consolidates most of the functionality that was previously spread across the major database access extensions, ie MySQL, PostgreSQL, SQLite, MSSQL, etc. The interface exposes high-level objects for the programmer to work with database connections, queries and result sets, and low-level drivers perform communication and resource handling with the database server. A lot of discussion and work is going into PDO and it's considered the appropriate method of working with databases in modern, professional code.

I find the above answers really lengthy, so to summarize:

The mysqli extension has a number of benefits, the key enhancements over the mysql extension being:

  • Object-oriented interface
  • Support for Prepared Statements
  • Support for Multiple Statements
  • Support for Transactions
  • Enhanced debugging capabilities
  • Embedded server support

Source: MySQLi overview


As explained in the above answers, the alternatives to mysql are mysqli and PDO (PHP Data Objects).

  • API supports server-side Prepared Statements: Supported by MYSQLi and PDO
  • API supports client-side Prepared Statements: Supported only by PDO
  • API supports Stored Procedures: Both MySQLi and PDO
  • API supports Multiple Statements and all MySQL 4.1+ functionality – Supported by MySQLi and mostly also by PDO

Both MySQLi and PDO were introduced in PHP 5.0, whereas MySQL was introduced prior to PHP 3.0. A point to note is that MySQL is included in PHP5.x though deprecated in later versions.

It's possible to define almost all mysql_* functions using mysqli or PDO. Just include them on top of your old PHP application, and it will work on PHP7. My solution here .

 <?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_* functions were depreciated (as of php 5.5 ) given the fact that better functions and code structures were developed. The fact that the function was depreciated means that no more effort will be placed into improving it in terms of performance and security, which means it is less future proof .

If you need more reasons:

  • mysql_* functions do not support prepared statements.
  • mysql_* functions do not support the binding of parameters.
  • mysql_* functions lack functionality for Object Oriented Programming.
  • the list goes on …