Php избежать sql инъекций

Как избежать SQL инъекций в PHP

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

// Никак не обрабатываем данные, получаемые от пользователей $unsafe_variable = $_POST['user_input']; // Затем эти данные отправляем в базу данных mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");

Опасность тут заключается в следующем: если пользователь отправит данные вроде таких: value’); DROP TABLE table_name;— , то итоговый запрос будет выглядеть так:

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table_name;--')

Этот запрос удалит таблицу table_name .

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

Использовать заранее подготовленные данные

Можно воспользоваться подготовленными операторами и обработанными таким образом запросами. Такие операторы SQL позволят вам определять типы передаваемых данных, фильтровать их.

Есть 2 варианта их использования:

    Используем PDO (PHP Data Objects):

// Конфигурируем будущий запрос, $q = $pdo->prepare('SELECT * FROM table_name WHERE name = :name'); // Выполняем запрос, установив связь с введёнными данными // $name - переменная с данными, которые связываются с оператором name $q->execute(array('name' => $name)); foreach ($q as $e) < // Производим манипуляции с результатами $e >
$q = $db->prepare('SELECT * FROM table_name WHERE name = ?'); // $name - переменная с данными, которые будут обрабатываться $q->bind_param('s', $name); // 's' определяет тип переменной => 'string' (строка) $q->execute(); $result = $q->get_result(); while ($row = $result->fetch_assoc()) < // Производим манипуляции с $row >

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

Например, в PostgreSQL есть pg_prepare() and pg_execute() .

Установить соединение с базой данных правильно

PDO по умолчанию эмулирует работу с подготовленными операторами для работы с MySQL. Поэтому важно отключить режим эмуляции, например так:

// Устанавливаем соединение $db = new PDO('mysql:dbname=mydatabase;host=127.0.0.1;charset=utf8', 'db_login', 'db_pass'); // Отключаем режим эмуляции для обработки данных // Включаем рабочий реальный режим работы PDO $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); // Включаем режим обработки ошибок и исключений // PHP Fatal error не будут обрывать работу скрипта, а исключения позволят отлавливать ошибки $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Готовим операторы // :column $q = $db->prepare('INSERT INTO table (column) VALUES (:column)'); // Связываем операторы с потенциально опасными данными // которые будут обработаны и отфильтрованы $q->execute(array('column' => $danger_data));

Важный момент касаемо параметра charset: версии PHP ? или именованный :name из примера выше, можно сообщить базе данных, что из данных должно быть обработано и отфильтровано. Затем во время вызова execute() указываются данные, которые будут проверяться.

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

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

Как использовать с динамическими запросами

Вы можете использовать следующий подход (составить белый лист параметов и пропускать только их):

// $order может быть ‘DESC’ // Во всех остальных случаях $order это ‘ASC’ if (empty($order) || $order !== ‘DESC’)

Источник

Php избежать sql инъекций

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

Пример #1 Постраничный вывод результата и создание суперпользователя в PostgreSQL

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

$offset = $_GET [ ‘offset’ ]; // осторожно, нет валидации ввода!
$query = «SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset ;» ;
$result = pg_query ( $conn , $query );
?>

Обычно пользователи нажимают по ссылкам ‘вперёд’ и ‘назад’, вследствие чего значение переменной $offset заносится в URL . Скрипт ожидает, что $offset — десятичное число. Однако, взломщик может попытаться взломать систему, присоединив к URL следующее значение:

0; insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd) select 'crack', usesysid, 't','t','crack' from pg_shadow where usename='postgres'; --

Если это произойдёт, скрипт предоставит злоумышленнику доступ суперпользователя. Обратите внимание, что значение 0; использовано для того, чтобы задать правильное смещение для первого запроса и корректно его завершить.

Замечание:

Это распространённый приём, чтобы заставить синтаксический анализатор SQL игнорировать остальную часть запроса, написанного разработчиком с помощью — , который является знаком комментария в SQL.

Ещё один вероятный способ получить пароли учётных записей в БД — атака страниц, предоставляющих поиск по базе. Злоумышленнику нужно лишь проверить, используется ли в запросе передаваемая на сервер и необрабатываемая надлежащим образом переменная. Это может быть один из устанавливаемых на предыдущей странице фильтров, таких как WHERE, ORDER BY, LIMIT и OFFSET , используемых при построении запросов SELECT . В случае, если используемая вами база данных поддерживает конструкцию UNION , злоумышленник может присоединить к оригинальному запросу ещё один дополнительный, для извлечения пользовательских паролей. Настоятельно рекомендуем использовать только зашифрованные пароли.

Пример #2 Листинг статей. и некоторых паролей (для любой базы данных)

$query = «SELECT id, name, inserted, size FROM products
WHERE size = ‘ $size ‘» ;
$result = odbc_exec ( $conn , $query );
?>

Статическая часть запроса может комбинироваться с другим SELECT -запросом, который выведет все пароли:

' union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable; --

Выражения UPDATE и INSERT также подвержены таким атакам.

Пример #3 От сброса пароля до получения дополнительных привилегий (любой сервер баз данных)

Но злоумышленник может ввести значение ‘ or uid like’%admin%’ для переменной $uid для изменения пароля администратора или просто присвоить переменной $pwd значение hehehe’, trusted=100, admin=’yes для получения дополнительных привилегий. При выполнении запросы переплетаются:

// $uid: ‘ or uid like ‘%admin%
$query = «UPDATE usertable SET pwd=’. ‘ WHERE uid=» or uid like ‘%admin%’;» ;

// $pwd: hehehe’, trusted=100, admin=’yes
$query = «UPDATE usertable SET pwd=’hehehe’, trusted=100, admin=’yes’ WHERE
. ;» ;
?>

Хотя остаётся очевидным, что для проведения успешной атаки злоумышленник должен обладать хотя бы некоторыми знаниями об архитектуре базы данных, получить эту информацию зачастую очень просто. Например, код может быть частью программного обеспечения с открытым исходным кодом и находиться в открытом доступе. Эта информация также может быть раскрыта закрытым кодом — даже если он закодирован, обфусцирован или скомпилирован, и даже вашим собственным кодом через отображение сообщений об ошибках. Другие методы включают использование типичных имён таблиц и столбцов. Например, форма входа в систему, использующая таблицу ‘users’ с именами столбцов ‘id’, ‘username’ и ‘password’.

Пример #4 Атака на операционную систему сервера базы данных (MSSQL Server)

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

$query = «SELECT * FROM products WHERE id LIKE ‘% $prod %'» ;
$result = mssql_query ( $query );
?>

Если злоумышленник передаст значение a%’ exec master..xp_cmdshell ‘net user test testpass /ADD’ — в $prod , то $query будет:

$query = «SELECT * FROM products
WHERE id LIKE ‘%a%’
exec master..xp_cmdshell ‘net user test testpass /ADD’ —%'» ;
$result = mssql_query ( $query );
?>

MSSQL Server выполняет SQL запросы в пакете, включая команду добавления нового пользователя в локальную базу данных учётных записей. Если бы это приложение было запущено от имени sa и служба MSSQLSERVER была запущена с достаточными привилегиями, у злоумышленника появилась бы учётная запись, с помощью которой он мог бы получить доступ к этой машине.

Замечание:

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

Забавный пример проблем, связанных с SQL-инъекциями

Способы защиты

Рекомендуемый способ избежать SQL-инъекций — связывание всех данных с помощью подготовленных запросов. Использование подготовленных запросов недостаточно для полного предотвращения SQL-инъекций, но это самый простой и безопасный способ обеспечить ввод данных в SQL-запросы. Все динамические литералы данных в выражениях WHERE , SET и VALUES должны быть заменены заполнителями. Фактические данные будут связаны во время выполнения и отправлены отдельно от команды SQL.

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

Пример #5 Избегание SQL-инъекций с помощью подготовленных операторов PDO

// Динамическая часть SQL проверяется на соответствие ожидаемым значениям
$sortingOrder = $_GET [ ‘sortingOrder’ ] === ‘DESC’ ? ‘DESC’ : ‘ASC’ ;
$productId = $_GET [ ‘productId’ ];

// SQL подготавливается с заполнителем
$stmt = $pdo -> prepare ( «SELECT * FROM products WHERE id LIKE ? ORDER BY price < $sortingOrder >» );

// Значение предоставляется с подстановочными знаками LIKE
$stmt -> execute ([ «% < $productId >%» ]);
?>

Подготовленные операторы предоставляются PDO, MySQLi, а также другими библиотеками баз данных.

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

  • Никогда не подключайтесь к базе данных как суперпользователь или владелец базы данных. Всегда используйте настроенных пользователей с минимальными привилегиями.
  • Всегда проверяйте введённые данные на соответствие ожидаемому типу. В PHP есть множество функций для проверки данных: начиная от простейших функций для работы с переменными и функций определения типа символов (таких как is_numeric() и ctype_digit() соответственно) и заканчивая Perl-совместимыми регулярными выражениями.
  • В случае, если приложение ожидает цифровой ввод, примените функцию ctype_digit() для проверки введённых данных, или принудительно укажите их тип при помощи settype() , или просто используйте числовое представление при помощи функции sprintf() .
  • Если на уровне базы данных не поддерживаются привязанные переменные, то всегда экранируйте любые нечисловые данные, используемый в запросах к БД при помощи специальных экранирующих функций, специфичных для используемой вами базы данных (например, mysql_real_escape_string() , sqlite_escape_string() и т.д.). Общие функции такие как addslashes() полезны только в определённых случаях (например MySQL в однобайтной кодировке с отключённым NO_BACKSLASH_ESCAPES ), поэтому лучше избегать их использование.
  • Ни в коем случае не выводите никакой информации о БД, особенно о её структуре. Также ознакомьтесь с соответствующими разделами документации: «Сообщения об ошибках» и «Функции обработки и логирования ошибок».

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

Источник

Читайте также:  Python получить код страницы request
Оцените статью