SQL инъекция — это один из самых доступных способов взлома сайта.
Суть таких инъекций – внедрение в данные (передаваемые через GET, POST запросы или значения Cookie) произвольного SQL кода. Если сайт уязвим и выполняет такие инъекции, то по сути есть возможность творить с БД (чаще всего это MySQL) что угодно.
Содержание
Как вычислить уязвимость, позволяющую внедрять SQL инъекции?
Довольно легко. Например, есть тестовый сайт test.ru. На сайте выводится список новостей, с возможностью детального просмотра. Адрес страницы с детальным описанием новости выглядит так: test.ru/?detail=1. Т.е через GET запрос переменная detail передаёт значение 1 (которое является идентификатором записи в таблице новостей).
Изменяем GET запрос на ?detail=1'
или ?detail=1"
. Далее пробуем передавать эти запросы серверу, т.е заходим на test.ru/?detail=1′ или на test.ru/?detail=1″.
Если при заходе на данные страницы появляется ошибка, значит сайт уязвим на SQL инъекции.
Возможные SQL инъекции (SQL внедрения)
1) Наиболее простые — сворачивание условия WHERE к истиностному результату при любых значениях параметров.
2) Присоединение к запросу результатов другого запроса. Делается это через оператор UNION.
3) Закомментирование части запроса.
Практика. Варианты взлома сайта с уязвимостью на SQL внедрения
Итак, у нас есть уже упоминавшийся сайт test.ru. В базе хранится 4 новости, 3 из которых выводятся. Разрешение на публикацию новости зависит от парметра public (если параметр содержит значение 1, то новость публикуется).
При обращении к странице test.ru/?detail=4, которая должна выводить четвёртую новость появляется ошибка – новость не найдена.
В нашем случае новость существует, но она запрещена к публикации.
Но так как мы уже проверяли сайт на уязвимость и он выдавал ошибку БД, то пробуем перебирать возможные варианты запросов.
В адресной строке плюс (+) выполняет роль пробела, так что не пугайтесь
Тестирую следующие варианты:
test.ru/?detail=4+OR+1
test.ru/?detail=4+—
test.ru/?detail=4+UNION+SELECT+*+FROM+news+WHERE+id=4
В итоге удача улыбнулась и два запроса (первый и третий) вернули нам детальное описание четвёртой новости
Разбор примера изнутри
За получение детального описания новости отвечает блок кода:
$detail_id=$_GET['detail'];
$zapros="SELECT * FROM `$table_news` WHERE `public`='1' AND `id`=$detail_id ORDER BY `position` DESC";
Мало того, что $detail_id получает значение без какой либо обработки, так ещё и конструкция `id`=$detail_id написана криво, лучше придерживаться `id`=’$detail_id’ (т.е сравниваемое значение писать в прямых апострофах).
Глядя на запрос, получаемый при обращении к странице через test.ru/?detail=4+OR+1
SELECT * FROM `news` WHERE `public`=’1′ AND `id`=4 OR 1 ORDER BY `position` DESC
становится не совсем ясно, почему отобразилась 4-ая новость. Дело в том, что запрос вернул все записи из таблицы новостей, отсортированные в порядке убывания сверху. И таким образом наша 4-ая новость оказалась самой первой, она же и вывелась как детальная. Т.е просто совпадение.
Разбираем запрос, сформированный при обращении через test.ru/?detail=4+UNION+SELECT+*+FROM+news+WHERE+id=4
.
Тут название таблицы с новостями (в нашем случае это news) бралось логическим перебором.
Итак, выполнился запрос SELECT * FROM `news` WHERE `public`='1' AND `id`=4 UNION SELECT * FROM news WHERE id=4 ORDER BY `position` DESC
. К нулевому результату первой части запроса (до UNION) присоединился результат второй части (после UNION), вернувшей детальное описание 4-ой новости.
Защита от SQL инъекций (SQL внедрений)
Защита от взлома сводится к базовому правилу «доверяй, но проверяй». Проверять нужно всё – числа, строки, даты, данные в специальных форматах.
Числа
Для проверки переменной на числовое значение используется функция is_numeric(n);, которая вернёт true, если параметр n — число, и false в противном случае.
Так же можно не проверять значение на число, а вручную переопределить тип. Вот пример, переопределяющий значение $id, полученное от $_GET[‘id_news’] в значение целочисленного типа (в целое число):
$id=(int)$_GET['id_news'];
Строки
Большинство взломов через SQL происходят по причине нахождения в строках «необезвреженных» кавычек, апострофов и других специальных символов. Для такого обезвреживания нужно использовать функцию addslashes($str);, которая возвращает строку $str с добавленным обратным слешем (\) перед каждым специальным символом. Данный процесс называется экранизацией.
$a="пример текста с апострофом ' ";
echo addslashes($a); //будет выведено: пример текста с апострофом \'
Кроме этого существуют две функции, созданные именно для экранизации строк, используемых в SQL выражениях.
Это mysql_escape_string($str); и mysql_real_escape_string($str);.
Первая не учитывает кодировку соединения с БД и может быть обойдена, а вот вторая её учитывает и абсолютно безопасна. mysql_real_escape_string($str); возвращает строку $str с добавленным обратным слешем к следующим символам: \x00, \n, \r, \, ', " и \x1a
.
Магические кавычки
Магические кавычки – эффект автоматической замены кавычки на обратный слэш (\) и кавычку при операциях ввода/вывода. В некоторых конфигурациях PHP этот параметр включён, а в некоторых нет. Для того, что бы избежать двойного экранизирования символов и заэкранизировать данные по-нормальному через mysql_real_escape_string($str);, необходимо убрать автоматические проставленные обратные слеши (если магические кавычки включены).
Проверка включённости магических кавычек для данных получаемых из GET, POST или Куков организуется через функцию get_magic_quotes_gpc();
(возвращает 1 – если магические кавычки включены, 0 – если отключены).
Если магические кавычки вкючены (т.е обратные слеши добавляеются) и такое встречается чаще, то их нужно убрать. Это делается через функцию stripslashes($str); (возвращает строку $str без обратных слешей у кавычек и прямых апострофов).
В закючении привожу код с полной экранизацией строк для записи в БД
if (get_magic_quotes_gpc()==1) { $element_title = stripslashes (trim( $_POST [ "element_title" ])); $element_text = stripslashes (trim( $_POST [ "element_text" ])); $element_date = stripslashes (trim( $_POST [ "element_date" ])); } else { $element_title =trim( $_POST [ "element_title" ]); $element_text =trim( $_POST [ "element_text" ]); $element_date =trim( $_POST [ "element_date" ]); } $element_title =mysql_real_escape_string( $element_title ); $element_text =mysql_real_escape_string( $element_text ); $element_date =mysql_real_escape_string( $element_date ); |
XSS
Межсайтовый скриптинг заключается в том, что атакующий пытается передать JavaScript или какой-либо другой код в веб-форму, чтобы выполнить вредоносный код на вашем веб-сайте.
При создании формы всегда проверяйте данные, которые к вам приходят от пользователей, и кодируйте или маскируйте любые спецсимволы.
- Используйте экранирование входных\выходных данных. Применяйте встроенные функции для очистки кода от вредоносных скриптов. К ним относятся такие функции как htmlspecialchar(), htmlentities() и strip_tags().
Примеры использования:
$name = strip_tags ( $_POST [ 'name' ]); $name = htmlentities( $_POST [ 'name' ], ENT_QUOTES, "UTF-8" ); $name = htmlspecialchars( $_POST [ 'name' ], ENT_QUOTES); |
2. Указывайте кодировку на каждой веб-странице. Для каждой веб-страницы необходимо указывать кодировку (например, ISO-8859-1 или UTF-8) до каких-либо пользовательских полей.
Пример использования:
<?php header( "Content-Type: text/html; charset=utf-8" ); ?> <!DOCTYPE html> <html> <head> <title>Сharset</title> <meta charset= "utf-8" > </head> |
Сообщения об ошибке
Будьте осторожны с количеством информации, которое вы выдаёте в ваших сообщениях об ошибке. Например, если у вас есть форма для входа на ваш веб-сайт, вы должны обдумать сообщение, которое вы выдадите пользователю в случае неудачной попытки авторизации.
Вы должны использовать общие фразы, вроде «Неправильное имя пользователя или пароль», и не указывать, в чём именно пользователь ошибся.
Если злоумышленник пытается подобрать имя пользователя и пароль, а сообщение об ошибке выдаёт, что одно из полей было верным, тогда он может сконцентрироваться на оставшемся поле, что упрощает его задачу
Проверка форм на стороне сервера
Проверка форм всегда должна производиться как на стороне браузера, так и на стороне сервера. Браузер может проверить простые ошибки, вроде незаполненного обязательного поля или текста, введённого в поле, требующее ввода числа.
Однако эти проверки могут быть обойдены, и вы должны проверять эти условия также и на стороне сервера. Отсутствие подобной проверки может привести к вставке вредоносного кода в вашу базу данных или нежелательным результатам работы веб-сайта.
Загрузки файлов
Предоставление пользователям возможности загружать файлы на ваш веб-сайт может быть связано с большим риском в плане безопасности, даже если речь идёт просто о смене аватара. Риск состоит в том, что любой загруженный файл, как бы безобидно он ни выглядел, может содержать скрипт, который при выполнении на вашем сервере открывает доступ к вашему сайту.
Если у вас на сайте есть форма загрузки файла, тогда вам нужно относиться ко всем загружаемым файлам с подозрением. Если вы позволяете пользователям загружать изображения, то при определении типа файла, вы не можете полагаться на расширение или mime-тип, так как они легко могут быть подделаны.
Даже открытие файла и чтение его заголовка или использование функций проверки размера изображения не являются стопроцентной гарантией безопасности. Большинство форматов изображений позволяют хранить комментарии, которые также могут содержать код PHP, который может быть выполнен на сервере.
Так что же вы можете сделать, чтобы предотвратить это? Обычно вам нужно запретить исполнение загружаемых файлов пользователями.
По умолчанию веб серверы не пытаются выполнять файлы с расширениями изображений, но не рекомендуется полагаться исключительно на расширение файла, так как известны случаи, когда файл «image.jpg.php» обходил эту проверку.
Вот некоторые варианты решения проблемы: переименование файла при загрузке, чтобы удостовериться в корректности разрешения; изменение разрешений для файла, например на chmod 0666, чтобы он не мог быть выполнен.
В *nix системах вы можете создать файл .htaccess (смотрите пример ниже), который откроет доступ только к заданному множеству файлов, что исключит возможность атаки с двойным расширением, упомянутой ранее:
deny from all order deny,allow allow from all |
Однако, рекомендуемое решение – полностью исключить прямой доступ к загружаемым файлам. При этом любые файлы, загружаемые на ваш веб-сайт, хранятся в папке вне корня сайта или в базе данных как большие двоичные объекты (BLOB).
Если ваши файлы недоступны напрямую, вам потребуется создать скрипт (или обработчик HTTP в .NET), чтобы извлекать их из закрытой папки и выдавать браузеру.
Тэги img поддерживают атрибут src, который в данном случае не будет являться прямым URL-адресом изображения, а будет указывать на скрипт извлечения файла. Также не забудьте указать в скрипте корректный HTTP заголовок content-type.
Например:
<img src= "/imageDelivery.php?id=1234" /> <?php // imageDelivery.php // Извлечение имени файла из БД соответственно $_GET["id"] ... // Выдача изображения браузеру Header('Content-Type: image/gif'); readfile('images/'.$fileName); ?> |
Большинство хостинг провайдеров производит необходимые настройки сервера за вас, но если ваш веб-сайт работает на вашем собственном сервере, тогда есть ещё несколько вещей, которые вам нужно проверить.
Убедитесь, что у вас настроен межсетевой экран, и он блокирует несущественные порты. Если это возможно, настройте DMZ (демилитаризованную зону), открыв доступ из внешнего мира только к портам 80 и 443. Хотя, это может быть невозможно, если вы не имеете доступа к вашему серверу из локальной сети, так как в этом случае вам придётся открыть порты, позволяющие загружать файлы и удалённо управлять вашим сервером через SSH или RDP.
Если вы позволяете загрузку файлов из интернета, используйте защищённые методы передачи, такие как SFTP или SSH.
Если это возможно, выделите отдельный сервер под базу данных, отличный от веб-сервера.
При этом сервер базы данных не будет напрямую доступен из внешнего мира, только ваш веб сервер сможет получить доступ к нему, тем самым минимизируя риск кражи ваших данных.
Наконец, не забудьте ограничить физический доступ к вашему серверу.
SSL
SSL – это протокол, использующийся для обеспечения безопасности при передаче данных по сети интернет. Это хорошая идея использовать сертификат безопасности каждый раз, когда вы передаёте персональную информацию между клиентом и веб сервером или базой данных.
Злоумышленники могут прослушивать канал связи и, если он не безопасен, перехватить пересылаемую информацию и использовать её для получения доступа к аккаунтам и персональной информации пользователей.