Thank you for reading this post, don't forget to subscribe!
Допустим, вы создаете типичный веб-сайт PHP для местного магазина электронной коммерции, поэтому вы решили добавить контактную форму, например:
[codesyntax lang="php"]
1 2 3 4 5 6 7 8 9 |
<form action="record_message.php" method="POST"> <label>Your name</label> <input type="text" name="name"> <label>Your message</label> <textarea name="message" rows="5"></textarea> <input type="submit" value="Send"> </form> |
[/codesyntax]
И давайте предположим, что файл send_message.php хранит все в базе данных, чтобы владельцы магазина могли читать пользовательские сообщения.
Например такой код:
[codesyntax lang="php"]
1 2 3 4 5 6 7 8 9 |
<?php $name = $_POST['name']; $message = $_POST['message']; // check if this user already has a message mysqli_query($conn, "SELECT * from messages where name = $name"); // Other code here |
[/codesyntax]
Итак, вы сначала пытаетесь увидеть, есть ли у этого пользователя непрочитанное сообщение. Запрос SELECT * from messages where name = $name, кажется достаточно простым, верно?
НЕПРАВИЛЬНО!
Теперь мы открыли двери для мгновенного уничтожения нашей базы данных.
Для этого злоумышленнику необходимо выполнить следующие условия:
- Приложение работает на базе данных SQL (сегодня почти каждое приложение работает на SQL)
- Текущее подключение к базе данных имеет разрешения «редактировать» и «удалять» в базе данных.
- Названия важных таблиц можно угадать
Третий момент означает, что теперь, когда злоумышленник знает, что вы управляете магазином электронной коммерции, вы, скорее всего, сохраните данные заказа в таблице orders.
Вооружившись всем этим, все, что нужно сделать злоумышленнику, это указать имя:
Joe; truncate orders;
Давайте посмотрим, каким будет запрос, когда он будет выполнен скриптом PHP:
SELECT * FROM messages WHERE name = Joe; truncate orders;
Хорошо, в первой части запроса есть синтаксическая ошибка (без кавычек вокруг «Joe»), но точка с запятой вынуждает движок MySQL начать интерпретацию нового: truncate orders.
Просто так, одним махом, вся история заказов исчезла!
Теперь, когда вы знаете, как работает SQL-инъекция, пришло время посмотреть, как это остановить. Для успешного внедрения SQL необходимо выполнить два условия:
- Скрипт PHP должен иметь права на изменение / удаление базы данных. Я думаю, что это верно для всех приложений, и вы не сможете сделать ваши приложения доступными только для чтения. И подумайте, что, даже если мы удалим все привилегии на изменение, SQL-инъекция все еще может позволить кому-то запускать запросы SELECT и просматривать всю базу данных, включая конфиденциальные данные. Другими словами, снижение уровня доступа к базе данных не работает, ведь ваше приложение все равно нуждается в этом.
- Пользовательский ввод обрабатывается. Единственный способ внедрения SQL-кода — это когда вы принимаете данные от пользователей. Еще раз, не практично останавливать все входные данные для вашего приложения только потому, что вы беспокоитесь о внедрении SQL.
Предотвращение внедрения SQL инъекуции в PHP
Теперь, учитывая, что соединения с базой данных, запросы и пользовательские вводы являются частью жизни БД, то как мы можем предотвратить внедрение SQL?
К счастью, это довольно просто, и есть два способа сделать это:
1) очистить пользовательский ввод и 2) использовать подготовленные операторы.
Пользовательский ввод
Если вы используете более старую версию PHP (5.5 или ниже, и это часто случается на виртуальном хостинге), было бы целесообразно выполнить весь пользовательский ввод с помощью функции mysql_real_escape_string ().
В основном то, что он делает, это удаление всех специальных символов в строке, так что они теряют свое значение при использовании базой данных.
Например, если у вас есть строка, похожая на строку «I’m a strin», злоумышленник может использовать символ одинарной кавычки (‘) для манипулирования создаваемым запросом к базе данных и вызывать SQL-инъекцию.
Запуск его через mysql_real_escape_string () приводит к появлению строки, которая добавляет обратную косую черту к одиночной кавычке, избегая ее.
В результате вся строка теперь передается в базу данных как безвредная строка вместо возможности участвовать в манипулировании запросами.
У этого подхода есть один недостаток: это действительно очень старая техника, которая согласуется со старыми формами доступа к базе данных в PHP.
Начиная с PHP 7, этой функции больше не существует, что подводит нас к следующему решению.
Используйте готовые операторы
Подготовленные операторы — это способ сделать запросы к базе данных более безопасными и надежными.
Идея состоит в том, что вместо отправки необработанного запроса в базу данных мы сначала сообщаем базе данных структуру запроса, который мы будем отправлять.
Это то, что мы подразумеваем под «подготовкой».
Как только оператор подготовлен, мы передаем информацию как параметризованные входные данные, чтобы база данных могла «заполнить пробелы», подключив входные данные к структуре запросов, которую мы отправляли ранее.
Это отнимает любую особую мощность, которую могут иметь входные данные, в результате чего они будут рассматриваться как простые переменные (или полезные нагрузки, если хотите) во всем процессе.
Вот как выглядят подготовленные операторы:
[codesyntax lang="php"]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
<?php $servername = "localhost"; $username = "username"; $password = "password"; $dbname = "myDB"; // Create connection $conn = new mysqli($servername, $username, $password, $dbname); // Check connection if ($conn->connect_error) { die("Connection failed: " . $conn->connect_error); } // prepare and bind $stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)"); $stmt->bind_param("sss", $firstname, $lastname, $email); // set parameters and execute $firstname = "John"; $lastname = "Doe"; $email = "john@example.com"; $stmt->execute(); $firstname = "Mary"; $lastname = "Moe"; $email = "mary@example.com"; $stmt->execute(); $firstname = "Julie"; $lastname = "Dooley"; $email = "julie@example.com"; $stmt->execute(); echo "New records created successfully"; $stmt->close(); $conn->close(); ?> |
[/codesyntax]
Я знаю, что этот процесс кажется излишне сложным, если вы новичок в подготовленных операторах, но концепция стоит затраченных усилий.
Для тех, кто уже знаком с расширением PDO PHP и использует его для создания готовых операторов, у меня есть небольшой совет.
Предупреждение: будьте осторожны при настройке PDO
Используя PDO для доступа к базе данных, мы можем впасть в ложное чувство безопасности. «Ах, хорошо, я использую PDO. Теперь мне не нужно думать ни о чем другом », — таково наше мышление.
Это правда, что PDO (или подготовленные MySQLi операторы) достаточно для предотвращения всевозможных SQL-атак, но вы должны быть осторожны при его настройке.
Обычно просто скопируйте и вставьте код из учебников или из ваших предыдущих проектов и двигайтесь дальше, но этот параметр может отменить все:
1 |
<span class="token variable">$dbConnection</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token scope">PDO<span class="token punctuation">::</span></span><span class="token constant">ATTR_EMULATE_PREPARES</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Решение простое: убедитесь, что для этой эмуляции установлено значение false.
1 |
<span class="token variable">$dbConnection</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token scope">PDO<span class="token punctuation">::</span></span><span class="token constant">ATTR_EMULATE_PREPARES</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Теперь PHP-скрипт вынужден использовать подготовленные операторы на уровне базы данных, предотвращая всевозможные SQL-инъекции.
Предотвращение SQLi с использованием WAF
Знаете ли вы, что вы также можете защитить веб-приложения от внедрения SQL с помощью WAF (брандмауэр веб-приложений)?
И не только SQL-инъекция, но и многие другие уязвимости уровня 7, такие как межсайтовый скриптинг, неправильная аутентификация, межсайтовая подделка, раскрытие данных и т. д.