Не используйте старый API MySQL
Существует несколько способов подключения к базе данных MySQL на PHP. Наиболее распространенными являются MySQL API, MySQLi API и PDO API (объекты данных PHP). Последние два поддерживают больше функций, чем старый mysql API, и более безопасны. Если вы используете старые функции «mysql_», вам следует остановиться и изучить новый API PDO. Эти старые функции mysql устарели и больше не поддерживаются в PHP 7.x.
Плохая практика:
$con = mysql_connect("localhost", "root", "mypass") or die("Не удалось подключиться: " . mysql_error()); mysql_select_db("tutorials"); $result = mysql_query("select * from tutorials"); echo "<h2>Вот список тем:</h2>"; while ($row = mysql_fetch_array($result)) { echo $row['name']."<br />"; } mysql_close($con);
Лучшая практика:
require_once('includes/conn.inc.php'); $sql= "SELECT name, age FROM employees WHERE company_id = 10"; $stmt = $pdo->query($sql); $row = $stmt->fetch(PDO::FETCH_ASSOC); echo $row['name']; echo $row[age];
Не экранируйте ввод от клиента с помощью экранирующих функций
Экранирование переменных запроса вручную с использованием mysql_real_escape_string считается небезопасным по двум причинам:
- Когда вы используете этот метод регулярно, вы обязательно пропустите его один раз. Все, что нужно злоумышленнику, – это одна лазейка, через которую он может внедрить свой код в ваши SQL-запросы.
- При использовании строковых переменных не забывайте использовать кавычки, что не очень естественно.
Вместо этого используйте подготовленные заявления (см. Дополнительную информацию ниже).
Не думайте, что операторы prepare всегда безопасны в PHP
Цель подготовленных операторов состоит в том, чтобы отделить запрос от данных, чтобы данные были правильно вставлены в параметры запроса без каких-либо опций манипулирования. В большинстве случаев подготовленные операторы считаются очень безопасными для использования, и это считается наилучшей практикой для ввода пользовательских параметров в запросы.
Пример кода:
require_once('includes/conn.inc.php'); $sql= "SELECT * FROM employees"; $stmt = $pdo->prepare($sql); $stmt->execute(); $result = $stmt->fetchAll(); foreach($result as $row){ echo " <li>{$row['employeeName']}</li>"; }
Так где же готовые заявления не хватает в PHP? Это тот случай, когда вы вводите пользовательский ввод в запрос, но подготовленные операторы не поддерживают это внедрение. Например, MySQL PDO не поддерживает введение параметра (используя заполнитель «?») В спецификаторе LIMIT. Кроме того, пользовательский ввод не может быть вставлен в виде имен таблиц или столбцов в запросе. Если вы используете подготовленные операторы в этих случаях, вам следует тщательно санировать данные вручную или, что лучше, использовать библиотеку, которая сделает это за вас и уже была протестирована.
Не думайте, что платформы ORM не подвержены атакам SQL-инъекций.
Использование ORM может быть большим. Это говорит о том, что это не означает, что он не подвержен атакам SQL-инъекций. Это правда, что сложнее внедрить код в запрос, сгенерированный ORM, но это не значит, что программист не может сделать ошибку, которая откроет лазейку. В большинстве случаев эту атаку можно выполнить при объединении пользовательского ввода в запрос ORM без использования подготовленных операторов. Да, платформы ORM, такие как Doctrine, предоставляют возможность использовать подготовленные операторы, поэтому используйте их. Никогда не объединяйте строки в запрос, будь то запрос ORM или необработанный запрос SQL.
Кстати, недостаточно следовать правилам – обязательно проверяйте использование и реализацию ORM после кодирования, чтобы убедиться, что он не открыт для каких-либо SQL-инъекций.
Плохая практика
В следующем примере кода вы можете видеть, что параметр, который был введен пользователем, объединяется с Doctrine DQL, который подвергает приложение внедрению SQL.
<?php // INSECURE $dql = "SELECT u FROM MyProject\Entity\User u WHERE u.status = '" . $_GET['status'] . "' ORDER BY " . $_GET['orderField'] . " ASC";
Плохая практика
Как рекомендуется для пользователей, не являющихся ORM, использование подготовленных операторов рекомендуется при использовании платформы ORM (например, Doctrine).
<?php $orderFieldWhitelist = array('email', 'username'); $orderField = "email"; if (in_array($_GET['orderField'], $orderFieldWhitelist)) { $orderField = $_GET['orderField']; } $dql = "SELECT u FROM MyProject\Entity\User u WHERE u.status = ?1 ORDER BY u." . $orderField . " ASC"; $query = $entityManager->createQuery($dql); $query->setParameter(1, $_GET['status']);
Не стоит недооценивать силу кодировки символов
Согласно OWASP (Open Security Application Project Project), отсутствие использования utf8mb4 откроет вам различные типы атак (вы можете узнать больше об обходе кодирования). Кроме того, вы должны регулярно использовать utf8mb4 в PHP и MySQL для лучшей и более стандартной многоязычной поддержки.
Пример кода
$dsn = 'mysql:host=example.com;dbname=testdb;port=3306;charset=utf8mb4';
Резюме
Мы рассмотрели 5 очень распространенных ошибок, которые делают многие разработчики PHP. Некоторые из них происходят из-за недостатка знаний (новые платформы доступны постоянно, убедитесь, что вы знаете о них). Некоторые ошибки случаются из-за недостатка опыта (читайте, читайте и читайте больше, чтобы убедиться, что вы используете самые безопасные и лучшие API). Удачи в вашем следующем проекте!