Недавно, сидя вечером дома, я обнаружил, что у меня нет рабочих задач. Это заставило мыслить об окружающем мире. И я вспомнил сайт одного заказчика, который раньше обслуживался у другого поставщика 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 – число, но никак не название столбца. Естественно, данный запрос затронет каждую строку в БД. Теперь атакующий с лёгкостью может войти под любым аккаунтом на сайте, включая, даже аккаунт администратора.

Как предотвратить?

  1. Всегда проверяйте валидность входящих данных. Числа должны быть числами, строки – строками (привет динамическая типизация PHP:)
  2. Фильтруйте специальные символы (кавычки, тире). В PHP есть для этого специальная функция: mysql_escape_string.
  3. Не передавайте данные прямо в запрос. Используйте подготовленные запросы, а лучше хранимые процедуры. Рассмотренный выше запрос будет лучше, если использовать подготовленный запрос:

$statement = $db->prepare(‘UPDATE users SET password = :password WHERE user_id = :id ‘);

$statement->bindValue(‘:password’, $password);

$statement->bindValue(‘:id, $id);

$statement->execute();

  1. Не давайте скрипту полный доступ на операции с БД. Зачем скрипту сайта визитки разрешение на операцию DROP?
  2. Не выводите системные сообщения об ошибках запроса. Это усложнит понимание структуры БД злоумышленником.