Защита от SQL-инъекций
Опубликовано: 4 апр 2011 в 23:34
Перевод: freeeeez
Что такое SQL-инъекция?
SQL-инъекция — это процесс, при котором кто-либо выполняет SQL-запрос к базе данных без вашего ведома, с целью нанесения ущерба. Данная техника заключается в эксплуатации уязвимостей программного кода. Это происходит только тогда, когда ваши запросы составлены с использованием внешних данных, которые принимаются от пользователя. В данной статье представлены техники защиты от SQL-инъекций. Я проиллюстрирую эту ситуацию в двух случаях, (1) когда данные, введенные пользователем, плохо фильтруются, и (2) когда они не соответствуют типу принимаемых данных и выполняется один или несколько запросов.Это становится возможным, когда у вас отсутствует фильтрация входных данных, ведь позователь может ввести все что угодно, даже дополнительные запросы к БД.
Пример 1. Выполнение одного SQL-запроса
<?php $sqlStatement = "SELECT * FROM customers where username='james';"; ?>Пример 2. Выполнение более нескольких SQL-запросов
<?php $sqlStatement = "DROP TABLE users; UPDATE customers SET age=0; DELETE FROM customers where id>0;"; ?>Теперь злоумышленник может объединить оба случая, выполнить запросы одновременно, удалить таблицу users и повредить таблицу customers.
В результате успешной SQL-инъекции наносится вред базе данных.
Причины SQL-инъекции
SQL-инъекция может возникнуть в следующих случаях:- Отсутствие фильтрации
- Неправильная обработка типов
- Уязвимости в базе данных сервера
- Условные ошибки
1. Отсутствие фильтрации
Представим, что есть модуль, который запрашивает адрес электронной почты пользователя, чтобы отправить ему временный пароль к почте, когда он забывает свой пароль. В этом случае, SQL-запрос выглядеть, примерно, так:<?php $sqlStatement = "SELECT * FROM users WHERE username = '" + $username + "' AND email = '" + $email + "' "; ?>Но хакер может изменить этот запрос, если установит значение переменной $email, добавив к адресу электронной почты удаление таблицы users:
user@hostname.com’; DROP TABLE users; SELECT * FROM customers WHERE name LIKE ‘%
В результате конечный запрос будет выглядеть так:<?php $sqlStatement = "SELECT * FROM users WHERE username = 'james' AND email = 'user@hostname.com'; DROP TABLE users; SELECT * FROM customers WHERE name LIKE '%'"; ?>Видите, как легко можно удалить любую таблицу из базы данных. Как результат, ваша система зависнет, поскольку ни один юзер не сможет войти в систему с этого момента. Если у вас нет резервной копии базы данных, вы потеряете все.
2. Неправильная обработка типов
Часто вы знаете тип данных, которые хотите получить. Например, возраст клиентов является числом, пол пользователя (муж/жен) строкой.А что, если кто-то введет $ageValue, как:
20; DROP TABLE users
В результате SQL запрос приобретет следующий вид:<?php $sqlStatement = "SELECT * FROM customers WHERE age = 20; DROP TABLE users;"; ?>Вы точно знаете, что значение $ageValue всегда будет числом. И чтобы злоумышленник не смог ввести что-то другое, необходимо проверять эту переменную.
3. Уязвимости в базе данных сервера
Хотя многие думают, что могут избежать SQL-инъекций, просто используя mysql_real_escape_string(), но они не правы, к сожалению. Встроенные функции для работы с базами данных поставляются вместе с языковым пакетом, а в некоторых прошлых версиях MySQL есть уязвимость в обработке многобайтового кода.4. Условные ошибки
Используя SQL-инъекцию пользователь может легко обойти вход в систему. Приведу пример:<?php $sqlStatement = "SELECT * FROM users WHERE username = 'james' AND password = 'secret' OR 1=1;"; ?>Значение 1=1 всегда истинно, таким образом, значение пароля уже не будет иметь значения и злоумышленник сможет войти в систему.
Методы предотвращения и защиты от MySQL-инъекций
- Использование параметризованных запросов
- Использование хранимых процедур
- Применение регулярных выражений
- Использование функций блокировки
- Отключение сообщений об ошибках
- Создание менее привилегированного пользователя
- Ограничения максимального значение
1. Использование параметризованных запросов
Вместо того, чтобы подставлять значения SQL-заявлений напрямую, подставляйте их параметризованные значения, следующим образом:<?php
$db_connection = new mysqli("localhost", "user", "pass", "db");
$statement = $db_connection->prepare("SELECT * FROM customers WHERE id = ?");
$statement->bind_param("i", $id);
$statement->execute();
?>
"i" - только целые числа (int)"d" - числа с плавающей запятой (double)
"s" - строки (string)
"b" - отправляется в пакетах (blob)
2. Использование хранимых процедур
Использование хранимых процедур, тоже может помочь снизить риск возникновения атаки. Использование процедур показано на следующем примере:<?php $sqlStatement = " CREATE PROCEDURE HUGEORDER ( id INT , quantity INT, price DECIMAL(6,2) ) BEGIN DECLARE discount_percent DECIMAL(6,2); DECLARE discounted_price DECIMAL(6,2); SET discount_percent = 10; SET discounted_price = price – discount_percent/100*price; IF quantity > 500 THEN SET discounted_price = discounted_price - 0.25 * quantity; END IF; UPDATE fashion_products SET product_price = discounted_price WHERE product_id = id; Select * from fashion_products; END; "; ?>
3. Применение регулярных выражений
Регулярные выражения используются для того, чтобы привести входные данные к одному шаблону. Например, здесь мы проверяем email клиента на валидность и отвергаем возможность для SQL-инъекций.<?php
if(!preg_match("/^[0-9a-z\_\.\-]+@([\-a-z0-9]+\.)+[a-z]{2,}$/i", $email))
{
echo 'INVALID Email Address!';
return;
}
?>
Также вы можете пользоваться встроенными функциями PHP is_array(), is_bool(), is_double(), is_float(), is_int(), is_integer() и другими, для проверки данных пользователя.
4. Использование функции блокировки
Используйте функцию mysql_real_escape_string() для обработки внешних данных. Например:<?php $username = mysql_real_escape_string($username, $ dbLink); ?>Это очень мощная встроенная функция PHP, способная предотвратить SQL-инъекции в большинстве случаев. Вы можете попробовать внедрить SQL-код после использования mysql_real_escape_string() и тестировать на уязвимости. Эта функция отвергает множество умных методов атак, используемых злоумышленниками.
5. Отключение сообщений об ошибках
Прежде всего избегайте встроенной MySQL функции mysql_error(). Умный взломщик может угадать некоторые параметры базы данных из сообщения об ошибке, а иногда и увидеть параметры соединения. Используйте mysql_error() только на стадии разработки. Но убирайте ее, когда запускаете сайт на сервере.Также отключите отчеты об ошибках в PHP. Это делается одной строкой:
<?php // Отключить вывод ошибок error_reporting(0); ?>А лучше создайте собственное сообщение об ошибке.
<?php
if(!mysql_query($statement))
{
echo 'Извините, но сервер не доступен!';
}
?>
В результате, пользователь не узнает из сообщения об ошибке никакой важной информации, такой как, имя базы данных, имя таблицы, имя пользователя и других. Тем самым мы усложняем хакеру возможность узнать структуру SQL-запроса, используя различные инъекции.
6. Создайте менее привилегированного пользователя БД
В большинстве случаев, вы заметите, что посетителям не нужно удалять или обновлять информацию. Представим интернет-магазин. Пользователь может запросить данные (SELECT) или оставить заказ (INSERT).Таким образом, лучше создать несколько различных пользователей. Для администратора предоставить все привилегии, а для обычного пользователя ограниченные. Пример соединения для различных пользователей:
<?php
$visitorDbLink = mysql_connect('host', 'general_user', 'general_user_pass');
$adminDbLink = mysql_connect('host', 'admin_user', 'admin_pass');
?>
Теперь можно использовать $visitorDbLink для регулирования доступа к базе данных для посетителей, и использовать $adminDbLink для доступа в качестве администратора.
7. Установите ограничения на максимальное значение
Если имя пользователя не может быть больше 10 символов, попробуйте использовать "maxlenght" свойство:<input name="username" type="text" id="username" maxlength="10" />Однако, это не совсем полезно, так как пользователь может скачать HTML-форму себе на компьютер и создать свою, более удобную форму, которая будет обрабатываться этим же скриптом. Тогда следует дублировать это ограничение на сервере:
<?php $name = substr($_POST['name'],0,10); ?>Функция substr() вернет значение name длиною 10 символов, начиная с нулевого.
Ну вот и все, что я хотел рассказать вам о защите от SQL-инъекции и безопасности баз данных. Надеюсь, что вам было интересно. Спасибо, что дочитали.
P.S. В статье Слепая SQL-инъекция я рассмотрел новые способы защиты от разновидности SQL-уязвимостей.
Источник: Learning Is Fun 
Тэги: sql-инъекция • php
9 комментариев
4 070 просмотров
#1 Кирилл, 7 апр 2011 в 19:37
#2 freeeeez, 7 апр 2011 в 19:51
#3 Санвел, 12 апр 2011 в 16:50
#4 Руслан, 23 июн 2011 в 18:51
#5 Руслан, 23 июн 2011 в 18:52
#6 freeeeez, 23 июн 2011 в 18:59
#7 Андрей, 16 сен 2011 в 00:30
#8 freeeeez, 16 сен 2011 в 08:56
#9 wew, 26 мар 2012 в 09:32






