Недавно, сидя вечером дома, я обнаружил, что у меня нет рабочих задач. Это заставило мыслить об окружающем мире. И я вспомнил сайт одного заказчика, который раньше обслуживался у другого поставщика IT-решений (назовем эту фирму — NON). У нас были данные доступа в админпанель. Немного поразмыслив, я обнаружил уязвимость в коде фирмы NON. Через час я взломал другой сайт этой фирмы, а еще через час и сайт самой компании NON.
Поэтому хочу поделится информацией о простой уязвимости, которая была у них обнаружена, и как ее избежать.
SQL инъекции происходят при внедрении данных в SQL запрос, в результате изменяющие структуру запроса до неожиданной структуры. Случайная SQL инъекция приведёт скорее к ошибке, чем к уязвимости.
Вредоносные данные могут получены не только от пользователя, но и получены приложением с другого источника, например, сгенерированы.
Однако SQL инъекция может привести к серьёзной уязвимости, используя которую, злоумышленник, при особой удаче и навыках, может получить информацию с базы данных либо даже изменить её.
Пример уязвимости на PHP
Следующий фрагмент кода предназначен для смены пароля пользователем:
$password = $_POST['password'];
$id = $_POST['id'];
$sql = «UPDATE users SET password = ‘$password’ WHERE user_id = $id»;
Предположим, атакующий передал следующие данные POST запросом:
password=12345 и id=account_id
В итоге запрос примет следующий вид:
UPDATE users SET password = ’12345′ WHERE user_id = user_id
Ожидалось, что user_id – число, но никак не название столбца. Естественно, данный запрос затронет каждую строку в БД. Теперь атакующий с лёгкостью может войти под любым аккаунтом на сайте, включая, даже аккаунт администратора.
Как предотвратить SQL инъекции такого типа
- Всегда проверяйте валидность входящих данных. Числа должны быть числами, строки – строками (привет динамическая типизация PHP:)
- Фильтруйте специальные символы (кавычки, тире). В PHP есть для этого специальная функция: mysql_escape_string.
- Не передавайте данные прямо в запрос. Используйте подготовленные запросы, а лучше хранимые процедуры. Рассмотренный выше запрос будет лучше, если использовать подготовленный запрос:
$statement = $db->prepare(‘UPDATE users SET password = :password WHERE user_id = :id ‘);
$statement->bindValue(‘:password’, $password);
$statement->bindValue(‘:id, $id);
$statement->execute();
- Не давайте скрипту полный доступ на операции с БД. Зачем скрипту сайта визитки разрешение на операцию DROP?
- Не выводите системные сообщения об ошибках запроса. Это усложнит понимание структуры БД злоумышленником.