HOME FORUMS MEMBERS RECENT POSTS LOG IN  
× Авторизация
Имя пользователя:
Пароль:
Нет аккаунта? Регистрация
Баннер 1   Баннер 2
НОВЫЕ ТОРГОВАЯ НОВОСТИ ЧАТ
loading...
Скрыть
Вернуться   ANTICHAT > БЕЗОПАСНОСТЬ И УЯЗВИМОСТИ > Уязвимости > Веб-уязвимости
   
Ответ
 
Опции темы Поиск в этой теме Опции просмотра

  #1  
Старый 02.01.2018, 13:13
Deniss Matjusevs
Новичок
Регистрация: 21.12.2017
Сообщений: 0
С нами: 4417679

Репутация: 0
По умолчанию

Как это понять?
Скрытое содержимое, Вам необходимо иметь сообщений: 0, а сейчас у Вас сообщений:2.
 
Ответить с цитированием

  #2  
Старый 02.01.2018, 14:21
r0hack
Постоянный
Регистрация: 11.12.2017
Сообщений: 512
С нами: 4432095

Репутация: 0


По умолчанию

Цитата:

Deniss Matjusevs сказал(а):

Как это понять?

То что ссылка закрыта от индексирования
 
Ответить с цитированием

  #3  
Старый 16.01.2018, 15:41
Citizen0
Новичок
Регистрация: 07.02.2017
Сообщений: 0
С нами: 4874484

Репутация: 0
По умолчанию

Цитата:

r0hack сказал(а):

PHP:


Код:
$id
=
$_GET
[
'id'
]
?
?
'Пусто'
;
$connect
=
new
PDO
(
'mysql:dbname=codeby;host=localhost'
,
'root'
,
''
)
;
$sql
=
"SELECT username, password FROM античат _sql WHERE id = :id"
;
$sth
=
$connect
-
>
prepare
(
$sql
,
[
PDO
:
:
ATTR_CURSOR
=
>
PDO
:
:
CURSOR_FWDONLY
]
)
;
$sth
-
>
execute
(
[
':id'
=
>
$id
]
)
;
while
(
$row
=
$sth
-
>
fetch
(
)
)
{
echo
'Username: '
.
$row
[
'username'
]
.
'
'
;
echo
'Password: '
.
$row
[
'password'
]
;
}
}
Последняя скобка лишняя

Цитата:

r0hack сказал(а):

PHP:


Код:
$id
=
$_GET
[
'id'
]
?
?
'Пусто'
;
$connect
=
new
mysqli
(
'localhost'
,
'root'
,
''
,
'codeby'
)
;
$query
=
"SELECT username, password FROM античат _sql WHERE id = ?"
;
$sth
=
$mysqli
-
>
stmt_init
(
)
;
if
(
$sth
-
>
prepare
(
$query
)
)
{
$sth
-
>
bind_param
(
"i"
,
$id
)
;
$sth
-
>
execute
(
)
;
$result
=
$sth
-
>
get_result
(
)
;
while
(
$row
=
$result
-
>
fetch_array
(
MYSQLI_NUM
)
)
{
echo
'Username: '
.
$row
[
'username'
]
.
'
'
;
echo
'Password: '
.
$row
[
'password'
]
;
}
}
- Откуда взялась переменная $mysqli?
- Если указываете MYSQLI_NUM, то нужно использовать числовые индексы

PHP:


Код:
echo
'Username: '
.
$row
[
0
]
.
'
'
;
echo
'Password: '
.
$row
[
1
]
;
 
Ответить с цитированием

  #4  
Старый 05.04.2018, 07:12
iskus
Новичок
Регистрация: 14.10.2012
Сообщений: 1
С нами: 7146326

Репутация: 0
По умолчанию

Цитата:

maxoun сказал(а):

В 21 веке SQL инъекция она не опасная, так как полно методов защиты.
Те же самые подготовленные запросы, или к примеру регулярные выражения.
Методами PHP. и т.д.

Что за бред ты несёшь? Может ты про слепые скули не слышал?
 
Ответить с цитированием

  #5  
Старый 01.01.2025, 16:55
r0hack
Постоянный
Регистрация: 11.12.2017
Сообщений: 512
С нами: 4432095

Репутация: 0


По умолчанию

SQL-инъекции остаются в топ-3 критических уязвимостей веб-приложений согласно OWASP 2024. Каждый день хакеры эксплуатируют эту уязвимость, получая доступ к базам данных тысяч сайтов. В этом руководстве разберем все современные методы защиты PHP-приложений от SQL-атак, от базовых prepared statements до продвинутых техник валидации. Вы получите готовые решения, которые можно применить в своих проектах уже сегодня.

Это вторая часть цикла статей о безопасности PHP. Первая часть посвящена защите от XSS-атак, рекомендую ознакомиться для комплексного понимания безопасности веб-приложений.
Содержание
  1. Что такое SQL-инъекция: типы и механизм работы
  2. 7 проверенных методов защиты от SQL-инъекций
  3. Примеры уязвимого и безопасного кода
  4. Реальные кейсы взлома и их предотвращение
  5. Инструменты для тестирования безопасности
  6. Чек-лист безопасности разработчика
  7. Частые вопросы (FAQ)
Что такое SQL-инъекция: типы и механизм работы
SQL-инъекция это метод атаки, при котором злоумышленник внедряет вредоносный SQL-код через пользовательский ввод, получая несанкционированный доступ к базе данных. Эта уязвимость возникает, когда приложение напрямую конкатенирует пользовательские данные с SQL-запросами без должной валидации и экранирования.
Основные типы SQL-инъекций
1. Classic SQL Injection (Union-based)
Злоумышленник использует оператор UNION для объединения результатов своего запроса с легитимным:

PHP:


Код:
// Уязвимый код
$id
=
$_GET
[
'id'
]
;
$query
=
"SELECT * FROM users WHERE id = $id"
;
// Атака: ?id=1 UNION SELECT username, password FROM admin


2. Blind SQL Injection (Boolean-based)
Атакующий извлекает данные побитово через true/false ответы:

PHP:


Код:
// Атака: ?id=1 AND SUBSTRING(password,1,1)='a'
// Если страница загружается нормально - первый символ пароля 'a'
3. Time-based Blind SQL Injection
Использует временные задержки для извлечения данных:

PHP:


Код:
// Атака: ?id=1 AND IF(SUBSTRING(password,1,1)='a', SLEEP(5), 0)
// Если страница грузится 5 секунд - первый символ 'a'
4. Error-based SQL Injection
Эксплуатирует сообщения об ошибках базы данных:

PHP:


Код:
// Атака провоцирует ошибку с выводом структуры БД
// ?id=1 AND extractvalue(1, concat(0x7e, version()))
5. Second-Order SQL Injection
Вредоносные данные сохраняются в БД и выполняются позже:

PHP:


Код:
// Регистрация пользователя: admin'--
// При следующем запросе происходит инъекция
7 проверенных методов защиты от SQL-инъекций
1. Prepared Statements с PDO (рекомендуемый метод)
PDO (PHP Data Objects) предоставляет унифицированный интерфейс для работы с различными СУБД. Prepared statements полностью разделяют SQL-логику от данных:

PHP:


Код:
 PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES => false, // Важно для настоящих prepared statements
];
try {
    $pdo = new PDO($dsn, $username, $password, $options);
} catch (PDOException $e) {
    // Логируем ошибку, не показываем пользователю
    error_log($e->getMessage());
    die('Ошибка подключения к базе данных');
}
// Пример безопасного запроса с именованными параметрами
function getUserById($pdo, $userId) {
    $sql = "SELECT id, username, email, created_at
            FROM users
            WHERE id = :user_id AND status = :status";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([
        ':user_id' => $userId,
        ':status' => 'active'
    ]);
    return $stmt->fetch();
}
// Пример с позиционными параметрами
function searchUsers($pdo, $search, $limit = 10) {
    $sql = "SELECT * FROM users
            WHERE username LIKE ?
            ORDER BY created_at DESC
            LIMIT ?";
    $stmt = $pdo->prepare($sql);
    $stmt->execute(["%$search%", $limit]);
    return $stmt->fetchAll();
}
// Массовая вставка данных
function insertMultipleUsers($pdo, $users) {
    $sql = "INSERT INTO users (username, email, password_hash)
            VALUES (:username, :email, :password)";
    $stmt = $pdo->prepare($sql);
    foreach ($users as $user) {
        $stmt->execute([
            ':username' => $user['username'],
            ':email' => $user['email'],
            ':password' => password_hash($user['password'], PASSWORD_DEFAULT)
        ]);
    }
}


2. Prepared Statements с MySQLi



MySQLi также поддерживает prepared statements, но с другим синтаксисом:

PHP:


Код:
connect_error) {
    error_log($mysqli->connect_error);
    die('Ошибка подключения');
}
// Установка кодировки - критично для безопасности
$mysqli->set_charset('utf8mb4');
// Пример безопасного SELECT запроса
function getUserByEmail($mysqli, $email) {
    $sql = "SELECT id, username, created_at FROM users WHERE email = ?";
    if ($stmt = $mysqli->prepare($sql)) {
        // 's' означает string, 'i' - integer, 'd' - double, 'b' - blob
        $stmt->bind_param("s", $email);
        $stmt->execute();
        $result = $stmt->get_result();
        $user = $result->fetch_assoc();
        $stmt->close();
        return $user;
    }
    return null;
}
// Пример с множественными параметрами
function updateUserProfile($mysqli, $userId, $username, $bio) {
    $sql = "UPDATE users SET username = ?, bio = ? WHERE id = ?";
    if ($stmt = $mysqli->prepare($sql)) {
        $stmt->bind_param("ssi", $username, $bio, $userId);
        $success = $stmt->execute();
        $stmt->close();
        return $success;
    }
    return false;
}
// Пример безопасной процедуры с транзакцией
function transferMoney($mysqli, $fromId, $toId, $amount) {
    $mysqli->autocommit(false);
    try {
        // Снимаем деньги
        $sql1 = "UPDATE accounts SET balance = balance - ? WHERE user_id = ? AND balance >= ?";
        $stmt1 = $mysqli->prepare($sql1);
        $stmt1->bind_param("did", $amount, $fromId, $amount);
        $stmt1->execute();
        if ($stmt1->affected_rows === 0) {
            throw new Exception("Недостаточно средств");
        }
        // Зачисляем деньги
        $sql2 = "UPDATE accounts SET balance = balance + ? WHERE user_id = ?";
        $stmt2 = $mysqli->prepare($sql2);
        $stmt2->bind_param("di", $amount, $toId);
        $stmt2->execute();
        $mysqli->commit();
        return true;
    } catch (Exception $e) {
        $mysqli->rollback();
        error_log($e->getMessage());
        return false;
    }
}
3. Валидация и санитизация входных данных
Валидация должна происходить на нескольких уровнях:

PHP:


Код:
 []];
        if ($min !== null) {
            $options['options']['min_range'] = $min;
        }
        if ($max !== null) {
            $options['options']['max_range'] = $max;
        }
        return filter_var($input, FILTER_VALIDATE_INT, $options);
    }
    // Валидация email
    public static function validateEmail($email) {
        $email = filter_var($email, FILTER_SANITIZE_EMAIL);
        return filter_var($email, FILTER_VALIDATE_EMAIL) ? $email : false;
    }
    // Валидация строк с whitelist символов
    public static function validateString($input, $pattern = '/^[a-zA-Z0-9_\-]+$/') {
        $input = trim($input);
        if (preg_match($pattern, $input)) {
            return $input;
        }
        return false;
    }
    // Валидация UUID
    public static function validateUUID($uuid) {
        $pattern = '/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i';
        return preg_match($pattern, $uuid) ? $uuid : false;
    }
    // Валидация дат
    public static function validateDate($date, $format = 'Y-m-d') {
        $d = DateTime::createFromFormat($format, $date);
        return $d && $d->format($format) === $date ? $date : false;
    }
    // Комплексная валидация для формы
    public static function validateUserForm($data) {
        $errors = [];
        $clean = [];
        // Username
        if (empty($data['username'])) {
            $errors['username'] = 'Username обязателен';
        } else {
            $username = self::validateString($data['username'], '/^[a-zA-Z0-9_]{3,20}$/');
            if (!$username) {
                $errors['username'] = 'Username может содержать только буквы, цифры и _ (3-20 символов)';
            } else {
                $clean['username'] = $username;
            }
        }
        // Email
        if (empty($data['email'])) {
            $errors['email'] = 'Email обязателен';
        } else {
            $email = self::validateEmail($data['email']);
            if (!$email) {
                $errors['email'] = 'Некорректный email';
            } else {
                $clean['email'] = $email;
            }
        }
        // Age
        if (isset($data['age'])) {
            $age = self::validateInt($data['age'], 13, 120);
            if ($age === false) {
                $errors['age'] = 'Возраст должен быть от 13 до 120 лет';
            } else {
                $clean['age'] = $age;
            }
        }
        return [
            'valid' => empty($errors),
            'data' => $clean,
            'errors' => $errors
        ];
    }
}
// Использование валидатора
$formData = $_POST;
$validation = InputValidator::validateUserForm($formData);
if ($validation['valid']) {
    // Используем очищенные данные
    $userData = $validation['data'];
    // Сохраняем в БД через prepared statements
} else {
    // Показываем ошибки
    foreach ($validation['errors'] as $field => $error) {
        echo "Ошибка в поле $field: $error
";
    }
}


4. Использование ORM (Eloquent, Doctrine)
ORM автоматически защищают от SQL-инъекций:

PHP:


Код:
first();
$users = User::where('status', 'active')
             ->where('created_at', '>=', now()->subDays(30))
             ->orderBy('name')
             ->paginate(10);
// Query Builder тоже безопасен
$users = DB::table('users')
           ->where('votes', '>', 100)
           ->orWhere('name', 'like', '%john%')
           ->get();
// Raw запросы с параметрами
$results = DB::select('SELECT * FROM users WHERE id = ?', [$id]);
$affected = DB::update('UPDATE users SET votes = 100 WHERE name = ?', [$name]);
// Пример с Doctrine ORM
use Doctrine\ORM\EntityManager;
class UserRepository {
    private $em;
    public function __construct(EntityManager $em) {
        $this->em = $em;
    }
    public function findActiveUsers($limit = 10) {
        $qb = $this->em->createQueryBuilder();
        return $qb->select('u')
                  ->from('App\Entity\User', 'u')
                  ->where('u.status = :status')
                  ->setParameter('status', 'active')
                  ->orderBy('u.createdAt', 'DESC')
                  ->setMaxResults($limit)
                  ->getQuery()
                  ->getResult();
    }
    public function searchUsers($term) {
        $query = $this->em->createQuery(
            'SELECT u FROM App\Entity\User u
             WHERE u.username LIKE :term OR u.email LIKE :term'
        );
        $query->setParameter('term', '%' . $term . '%');
        return $query->getResult();
    }
}
5. Хранимые процедуры
Хранимые процедуры добавляют дополнительный уровень защиты:

PHP:


Код:
prepare("CALL GetUserById(:userId)");
    $stmt->bindParam(':userId', $userId, PDO::PARAM_INT);
    $stmt->execute();
    return $stmt->fetch();
}
// Хранимая процедура с OUTPUT параметром
$createProcedure2 = "
CREATE PROCEDURE AuthenticateUser(
    IN userEmail VARCHAR(255),
    IN userPassword VARCHAR(255),
    OUT userId INT,
    OUT authResult BOOLEAN
)
BEGIN
    DECLARE storedPassword VARCHAR(255);
    SELECT id, password_hash INTO userId, storedPassword
    FROM users
    WHERE email = userEmail
    LIMIT 1;
    IF userId IS NOT NULL AND PASSWORD_VERIFY(userPassword, storedPassword) THEN
        SET authResult = TRUE;
    ELSE
        SET authResult = FALSE;
        SET userId = NULL;
    END IF;
END;
";
// Использование процедуры с OUTPUT
function authenticateUser($mysqli, $email, $password) {
    $stmt = $mysqli->prepare("CALL AuthenticateUser(?, ?, @userId, @authResult)");
    $stmt->bind_param("ss", $email, $password);
    $stmt->execute();
    $select = $mysqli->query("SELECT @userId, @authResult");
    $result = $select->fetch_assoc();
    return [
        'authenticated' => (bool)$result['@authResult'],
        'user_id' => $result['@userId']
    ];
}
6. Принцип наименьших привилегий
Настройка правильных прав доступа к БД:

PHP:


Код:
 PDO::ERRMODE_EXCEPTION]
            );
        }
        return self::$connections['read'];
    }
    // Пользователь для записи
    public static function getWriteConnection() {
        if (!isset(self::$connections['write'])) {
            self::$connections['write'] = new PDO(
                'mysql:host=localhost;dbname=myapp;charset=utf8mb4',
                'app_writer',  // Пользователь с правами INSERT, UPDATE, DELETE
                'write_password',
                [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
            );
        }
        return self::$connections['write'];
    }
    // Административный пользователь (используется редко)
    public static function getAdminConnection() {
        // Дополнительная проверка прав
        if (!self::isAdminContext()) {
            throw new Exception("Доступ запрещен");
        }
        if (!isset(self::$connections['admin'])) {
            self::$connections['admin'] = new PDO(
                'mysql:host=localhost;dbname=myapp;charset=utf8mb4',
                'app_admin',
                'admin_password',
                [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
            );
        }
        return self::$connections['admin'];
    }
    private static function isAdminContext() {
        // Проверка, что код выполняется в административном контексте
        return defined('ADMIN_CONTEXT') && ADMIN_CONTEXT === true;
    }
}
// SQL для создания пользователей с ограниченными правами
/*
-- Пользователь для чтения
CREATE USER 'app_reader'@'localhost' IDENTIFIED BY 'read_password';
GRANT SELECT ON myapp.* TO 'app_reader'@'localhost';
-- Пользователь для записи
CREATE USER 'app_writer'@'localhost' IDENTIFIED BY 'write_password';
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'app_writer'@'localhost';
-- Административный пользователь
CREATE USER 'app_admin'@'localhost' IDENTIFIED BY 'admin_password';
GRANT ALL PRIVILEGES ON myapp.* TO 'app_admin'@'localhost';
FLUSH PRIVILEGES;
*/
7. Web Application Firewall (WAF) и мониторинг
Реализация простого WAF на уровне приложения:

PHP:


Код:
/i',
        '/javascript:/i',
        '/onerror\s*=/i',
        '/onload\s*=/i',
        '/eval\s*\(/i',
        '/base64_decode/i'
    ];
    private $logger;
    public function __construct($logger = null) {
        $this->logger = $logger;
    }
    public function checkRequest($input) {
        $threats = [];
        // Проверяем все входящие данные
        $checkData = is_array($input) ? json_encode($input) : (string)$input;
        foreach ($this->suspiciousPatterns as $pattern) {
            if (preg_match($pattern, $checkData)) {
                $threats[] = $pattern;
            }
        }
        if (!empty($threats)) {
            $this->logThreat($threats, $input);
            return false;
        }
        return true;
    }
    private function logThreat($threats, $input) {
        $logData = [
            'timestamp' => date('Y-m-d H:i:s'),
            'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
            'threats' => $threats,
            'input' => $input,
            'uri' => $_SERVER['REQUEST_URI'] ?? 'unknown'
        ];
        // Логирование в файл или SIEM систему
        if ($this->logger) {
            $this->logger->warning('SQL Injection attempt detected', $logData);
        } else {
            error_log('SQL Injection attempt: ' . json_encode($logData));
        }
        // Можно добавить блокировку IP после N попыток
        $this->checkForRepeatedAttempts($_SERVER['REMOTE_ADDR']);
    }
    private function checkForRepeatedAttempts($ip) {
        // Реализация rate limiting и блокировки
        $cacheKey = "sql_attempts_$ip";
        $attempts = apcu_fetch($cacheKey) ?: 0;
        $attempts++;
        apcu_store($cacheKey, $attempts, 3600); // Хранить 1 час
        if ($attempts > 5) {
            // Блокировка IP или отправка алерта
            $this->blockIp($ip);
        }
    }
    private function blockIp($ip) {
        // Добавление в blacklist
        file_put_contents('/var/www/blacklist.txt', "$ip\n", FILE_APPEND | LOCK_EX);
        // Или использование iptables
        // exec("iptables -A INPUT -s $ip -j DROP");
    }
}
// Использование
$firewall = new SimpleSQLFirewall();
// Проверка всех входящих данных
$allInput = array_merge($_GET, $_POST, $_COOKIE);
if (!$firewall->checkRequest($allInput)) {
    http_response_code(403);
    die('Подозрительная активность заблокирована');
}
Дополнительный уровень защиты: Помимо WAF, рекомендую настроить Content Security Policy (CSP) для предотвращения XSS-атак, которые часто используются в связке с SQL-инъекциями. Подробное руководство по настройке CSP доступно в статье для начинающих.
Примеры уязвимого и безопасного кода



Пример 1: Аутентификация пользователя

PHP:


Код:
query($query);
    if ($result->num_rows > 0) {
        return $result->fetch_assoc();
    }
    return false;
}
// Атака: username = admin' --
// Результат: вход без пароля
// ✅ БЕЗОПАСНЫЙ КОД
function authenticateUserSecure($pdo, $username, $password) {
    $sql = "SELECT id, username, password_hash FROM users WHERE username = :username LIMIT 1";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([':username' => $username]);
    $user = $stmt->fetch();
    if ($user && password_verify($password, $user['password_hash'])) {
        // Удаляем хэш пароля из результата
        unset($user['password_hash']);
        return $user;
    }
    return false;
}
Пример 2: Поиск по сайту

PHP:


Код:
query($query);
}
// Атака: search = %' UNION SELECT * FROM credit_cards --
// ✅ БЕЗОПАСНЫЙ КОД
function searchProductsSecure($pdo, $search, $category = null, $limit = 20) {
    // Базовый запрос
    $sql = "SELECT id, name, price, description, image_url
            FROM products
            WHERE (name LIKE :search OR description LIKE :search)";
    $params = [':search' => "%$search%"];
    // Динамическое построение запроса
    if ($category) {
        $sql .= " AND category_id = :category";
        $params[':category'] = $category;
    }
    $sql .= " ORDER BY relevance DESC LIMIT :limit";
    $stmt = $pdo->prepare($sql);
    // Bind параметров с указанием типа
    foreach ($params as $key => $value) {
        if ($key === ':limit') {
            $stmt->bindValue($key, $value, PDO::PARAM_INT);
        } else {
            $stmt->bindValue($key, $value);
        }
    }
    $stmt->execute();
    return $stmt->fetchAll();
}
Пример 3: Динамическая сортировка

PHP:


Код:
query($query);
}
// Атака: sort = (SELECT password FROM admin)
// ✅ БЕЗОПАСНЫЙ КОД
function getUsersSecure($pdo, $sort = 'created_at', $direction = 'DESC') {
    // Whitelist разрешенных полей для сортировки
    $allowedSorts = [
        'created_at' => 'created_at',
        'username' => 'username',
        'email' => 'email',
        'last_login' => 'last_login'
    ];
    $allowedDirections = ['ASC', 'DESC'];
    // Валидация параметров сортировки
    $sortField = $allowedSorts[$sort] ?? 'created_at';
    $sortDirection = in_array(strtoupper($direction), $allowedDirections)
                     ? strtoupper($direction)
                     : 'DESC';
    // Безопасный запрос
    $sql = "SELECT id, username, email, created_at
            FROM users
            WHERE status = :status
            ORDER BY {$sortField} {$sortDirection}";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([':status' => 'active']);
    return $stmt->fetchAll();
}
Реальные кейсы взлома и их предотвращение
Кейс 1: Взлом интернет-магазина через корзину
Сценарий атаки:
В 2023 году хакеры взломали популярный интернет-магазин через уязвимость в функции обновления корзины. Код выглядел так:

PHP:


Код:
// Уязвимый код магазина
$product_id
=
$_POST
[
'product_id'
]
;
$quantity
=
$_POST
[
'quantity'
]
;
$user_id
=
$_SESSION
[
'user_id'
]
;
$query
=
"UPDATE cart SET quantity = $quantity WHERE product_id = $product_id AND user_id = $user_id"
;
mysqli_query
(
$connection
,
$query
)
;
Эксплуатация:
Хакеры изменили запрос, установив отрицательную цену товара:

Код:


Код:
POST: quantity = 1, price = -1000 WHERE product_id = 1 OR 1=1 --
Решение:

PHP:


Код:
 ['min_range' => 0, 'max_range' => 99]
        ]);
        if ($quantity === false) {
            throw new InvalidArgumentException('Некорректное количество товара');
        }
        // Проверка существования товара
        $checkSql = "SELECT id, price, stock FROM products WHERE id = :product_id";
        $checkStmt = $this->pdo->prepare($checkSql);
        $checkStmt->execute([':product_id' => $productId]);
        $product = $checkStmt->fetch();
        if (!$product) {
            throw new Exception('Товар не найден');
        }
        if ($product['stock'] pdo->prepare($updateSql);
        return $stmt->execute([
            ':user_id' => $userId,
            ':product_id' => $productId,
            ':quantity' => $quantity,
            ':price' => $product['price']
        ]);
    }
}
Кейс 2: Утечка базы данных через API endpoint
Сценарий атаки:
API для мобильного приложения имел уязвимость в endpoint получения профиля:

PHP:


Код:
// Уязвимый API endpoint
Route
:
:
get
(
'/api/user/{id}'
,
function
(
$id
)
{
$user
=
DB
:
:
select
(
"SELECT * FROM users WHERE id = $id"
)
;
return
response
(
)
-
>
json
(
$user
)
;
}
)
;
Эксплуатация:

Код:


Код:
GET /api/user/1 UNION SELECT credit_card_number, cvv, expiry FROM payment_methods
Решение:

PHP:


Код:
json(['error' => 'Invalid user ID'], 400);
        }
        // Проверка авторизации
        if (!$this->canAccessProfile($request->user(), $id)) {
            return response()->json(['error' => 'Access denied'], 403);
        }
        // Безопасный запрос с ограниченными полями
        $user = User::select(['id', 'username', 'email', 'created_at'])
                    ->where('id', $id)
                    ->where('status', 'active')
                    ->first();
        if (!$user) {
            return response()->json(['error' => 'User not found'], 404);
        }
        // Логирование доступа
        $this->logAccess($request->user()->id, $id);
        // Добавление rate limiting заголовков
        return response()->json($user)
                        ->header('X-RateLimit-Limit', 60)
                        ->header('X-RateLimit-Remaining', $this->getRemainingRequests());
    }
    private function canAccessProfile($currentUser, $targetId) {
        // Пользователь может видеть только свой профиль
        // или если он админ
        return $currentUser->id == $targetId || $currentUser->isAdmin();
    }
    private function logAccess($accessorId, $targetId) {
        DB::table('api_access_logs')->insert([
            'accessor_id' => $accessorId,
            'target_id' => $targetId,
            'endpoint' => 'user_profile',
            'ip' => request()->ip(),
            'created_at' => now()
        ]);
    }
}
Инструменты для тестирования безопасности
1. SQLMap - автоматическое тестирование





Bash:


Код:
# Базовое сканирование
sqlmap -u
"http://example.com/page.php?id=1"
--batch
# Сканирование с cookie
sqlmap -u
"http://example.com/page.php?id=1"
--cookie
=
"PHPSESSID=abc123"
# Сканирование POST запросов
sqlmap -u
"http://example.com/login.php"
--data
=
"username=admin&password=test"
# Получение структуры БД
sqlmap -u
"http://example.com/page.php?id=1"
--tables
# Дамп конкретной таблицы
sqlmap -u
"http://example.com/page.php?id=1"
-D database -T
users
--dump
2. Тестирование вручную

PHP:


Код:
testPayloads as $payload) {
            $testUrl = $url . "?$parameter=" . urlencode($payload);
            $startTime = microtime(true);
            $response = @file_get_contents($testUrl);
            $responseTime = microtime(true) - $startTime;
            $results[] = [
                'payload' => $payload,
                'response_time' => $responseTime,
                'error_detected' => $this->checkForSQLErrors($response),
                'status_code' => $http_response_header[0] ?? 'unknown'
            ];
        }
        return $results;
    }
    private function checkForSQLErrors($response) {
        $errorPatterns = [
            '/SQL syntax/',
            '/mysql_fetch/',
            '/Warning.*mysql/',
            '/MySQLSyntaxErrorException/',
            '/PostgreSQL/',
            '/valid MySQL result/',
            '/mssql_/',
            '/SQLServer JDBC Driver/',
            '/Oracle error/',
            '/Oracle driver/'
        ];
        foreach ($errorPatterns as $pattern) {
            if (preg_match($pattern, $response)) {
                return true;
            }
        }
        return false;
    }
}
3. Burp Suite настройка для PHP
Конфигурация Burp Suite для тестирования PHP приложений:
  1. Scanner настройки:
    • Включить "SQL Injection" в активных проверках
    • Установить insertion points на все параметры
    • Использовать грамотные payloads для PHP/MySQL
  2. Intruder настройки:
    • Payload type: Simple list
    • Добавить специфичные для PHP payloads
    • Настроить grep-match для PHP ошибок
  3. Расширения для PHP:
    • SQLiPy Sqlmap Integration
    • PHP Object Injection Check
    • Autorize для проверки авторизации
Чек-лист безопасности разработчика
Обязательные проверки перед деплоем
  • Все SQL запросы используют prepared statements
    • PDO или MySQLi с параметризацией
    • Никаких прямых конкатенаций с пользовательским вводом
  • Валидация всех входных данных
    • Whitelist подход для разрешенных символов
    • Проверка типов данных
    • Ограничение длины строк
  • Правильная обработка ошибок
    • Отключен вывод ошибок в продакшене
    • Логирование ошибок в файлы
    • Общие сообщения для пользователей
  • Настроены права доступа к БД
    • Разные пользователи для чтения/записи
    • Минимальные необходимые привилегии
    • Отключен FILE privilege
  • Включены механизмы защиты
    • WAF или правила фильтрации
    • Rate limiting для API
    • Мониторинг подозрительной активности
  • Регулярные обновления
    • PHP версии 8.0+
    • Актуальные версии MySQL/PostgreSQL
    • Обновленные библиотеки и фреймворки
  • Тестирование безопасности
    • Автоматические тесты на SQL инъекции
    • Ручное тестирование критических endpoint'ов
    • Регулярный аудит кода
Конфигурация php.ini для безопасности

INI:


Код:
; Отключение опасных функций
disable_functions
= exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source
; Отключение отображения ошибок
display_errors
= Off
log_errors
= On
error_log
= /var/log/php/error.log
; Ограничения
max_execution_time
= 30
max_input_time
= 60
memory_limit
= 128M
post_max_size
= 8M
upload_max_filesize
= 2M
; Безопасность сессий
session.cookie_httponly
= 1
session.cookie_secure
= 1
session.use_only_cookies
= 1
session.use_strict_mode
= 1
; Другие настройки безопасности
expose_php
= Off
allow_url_fopen
= Off
allow_url_include
= Off
Частые вопросы (FAQ)
Что такое SQL инъекция простыми словами?
SQL инъекция это уязвимость, позволяющая злоумышленнику выполнять произвольные SQL команды в базе данных через веб-приложение. Представьте, что ваше приложение это охранник, который проверяет пропуска (SQL запросы) на входе в здание (база данных). SQL инъекция это когда злоумышленник подделывает пропуск так, что охранник не только пускает его, но и дает ключи от всех помещений.
Как проверить свой сайт на SQL инъекции?
Базовая проверка включает:
  1. Добавление одинарной кавычки (') в параметры URL
  2. Использование SQLMap для автоматического сканирования
  3. Проверка форм с payloads типа
    Код:
    ' OR '1'='1
  4. Мониторинг логов на SQL ошибки
  5. Использование онлайн сканеров безопасности
Достаточно ли использовать mysqli_real_escape_string?
Нет,
Код:
mysqli_real_escape_string()
не является достаточной защитой. Эта функция экранирует специальные символы, но не защищает от всех типов инъекций, особенно в числовых полях. Всегда используйте prepared statements как основной метод защиты.
Какая разница между PDO и MySQLi для защиты?
Оба расширения поддерживают prepared statements и при правильном использовании одинаково безопасны. PDO имеет преимущества: поддержка разных СУБД, именованные параметры, более удобный API. MySQLi работает только с MySQL, но может быть быстрее для специфичных MySQL операций.
Можно ли делать динамические запросы безопасно?
Да, но с ограничениями:
  • Используйте whitelist для имен таблиц и колонок
  • Параметризируйте все значения через prepared statements
  • Никогда не конкатенируйте пользовательский ввод напрямую
  • Валидируйте все входные данные
Защищают ли ORM от SQL инъекций автоматически?
Современные ORM (Eloquent, Doctrine) защищают от SQL инъекций при использовании их стандартных методов. Однако, raw queries и неправильное использование могут создать уязвимости. Всегда параметризируйте raw запросы и избегайте динамического построения SQL.
Как часто нужно проводить аудит безопасности?
Рекомендуемая периодичность:
  • Перед каждым major релизом
  • После значительных изменений в коде работы с БД
  • Минимум раз в квартал для критических систем
  • При обнаружении новых типов атак
Как защититься от комплексных атак (SQL + CSRF + XSS)?
Современные атаки часто используют комбинацию уязвимостей. Например, через SQL-инъекцию злоумышленник может внедрить XSS-код в базу данных, который затем будет выполнен у всех пользователей (Stored XSS). Для полной защиты необходимо:
  • Использовать prepared statements для всех SQL запросов
  • Экранировать вывод данных из БД
  • Внедрить CSRF-токены для защиты от межсайтовой подделки запросов
Для углубленного изучения CSRF-атак рекомендую ознакомиться с анализом современных web-уязвимостей и методов обхода защиты, где подробно разобраны техники эксплуатации и защиты.
Что делать при обнаружении SQL инъекции в продакшене?
План действий:
  1. Немедленно изолировать уязвимый функционал
  2. Проанализировать логи на предмет эксплуатации
  3. Применить временный фикс (WAF правило)
  4. Разработать и протестировать постоянное решение
  5. Провести аудит на похожие уязвимости
  6. Уведомить пользователей если были скомпрометированы данные
Заключение
Защита от SQL инъекций требует комплексного подхода: от правильного написания кода до настройки инфраструктуры. Используйте prepared statements как основу защиты, добавляйте валидацию данных, применяйте принцип наименьших привилегий и регулярно тестируйте безопасность. Помните, что безопасность это не разовая задача, а постоянный процесс.

Начните с внедрения prepared statements во всех новых проектах, постепенно рефакторьте старый код и обязательно настройте мониторинг подозрительной активности. Современные инструменты и фреймворки делают защиту от SQL инъекций проще, но требуют понимания принципов безопасности для правильного применения.
Полезные ресурсы для углубленного изучения
  • OWASP SQL Injection Prevention Cheat Sheet
  • PHP Security Guide
  • Серия статей SQL-Injection для начинающих
  • Мощный, полный мануал по эксплуатации
  • SQL обучение
  • Практика: root-me.org, alexbers.com/sql/, dvwa.co.uk
  • PDO Prepared Statements
  • Всё про PDO
Начало здесь: Безопасный PHP. Защита от XSS атак
 
Ответить с цитированием
Ответ





Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 


Быстрый переход




ANTICHAT ™ © 2001- Antichat Kft.