27

Как предотвратить SQL-инъекции в PHP?

18

Описание проблемы

Я столкнулся с проблемой SQL-инъекций в своем приложении. Когда пользовательский ввод вставляется в SQL-запрос без предварительной обработки, это делает систему уязвимой к атакам. Я привожу ниже пример кода, где данная уязвимость имеется:

$unsafe_variable = $_POST['user_input']; 

mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");

В этом примере пользователь может ввести такой текст, как value'); DROP TABLE table;--, и запрос превратится в следующий:

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

Это позволяет злоумышленникам выполнять произвольные SQL-команды, что может привести к потере данных или другим нежелательным последствиям.

Как я могу защитить свое приложение от подобных уязвимостей? Какие лучшие практики могут помочь предотвратить SQL-инъекции?

4 ответ(ов)

3

Как бы вы ни поступили, обязательно проверьте, не была ли ваша входная информация уже нарушена magic_quotes или другим подобным "добрым" инструментом. Если это так, вам, возможно, придется обработать ее с помощью stripslashes или другого подходящего метода для очистки данных.

2

Если возможно, приводите типы ваших параметров. Однако это работает только с простыми типами, такими как int, bool и float.

$unsafe_variable = $_POST['user_id'];

$safe_variable = (int)$unsafe_variable;

mysqli_query($conn, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

В этом примере мы приводим переменную $unsafe_variable, полученную из массива $_POST, к целочисленному типу с помощью (int). Это помогает предотвратить некоторые типы атак, такие как SQL-инъекции, так как мы гарантируем, что в запросе используется именно целое число. Тем не менее, рекомендуется также использовать подготовленные выражения для повышения безопасности при работе с базой данных.

1

Вопросы о PHP и MySQL действительно очень распространены, но вот пример кода для PHP и Oracle, который показывает, как предотвратить SQL-инъекции и использовать драйверы oci8:

$conn = oci_connect($username, $password, $connection_string);
$stmt = oci_parse($conn, 'UPDATE table SET field = :xx WHERE ID = 123');
oci_bind_by_name($stmt, ':xx', $fieldval);
oci_execute($stmt);

Этот код использует подготовленные выражения и связывает параметры, что помогает защитить ваш код от SQL-инъекций. Также не забудьте обрабатывать возможные ошибки соединения и выполнения запросов для повышения надежности вашего приложения.

0

Вы написали функцию sqlvprintf, которая позволяет формировать SQL-запросы с использованием синтаксиса, похожего на String.Format в C#. Вот основной функционал вашей функции:

function sqlvprintf($query, $args)
{
    global $DB_LINK;
    $ctr = 0;
    ensureConnection(); // Подключаемся к базе данных, если еще не подключены.
    $values = array();
    foreach ($args as $value)
    {
        if (is_string($value))
        {
            $value = "'" . mysqli_real_escape_string($DB_LINK, $value) . "'";
        }
        else if (is_null($value))
        {
            $value = 'NULL';
        }
        else if (!is_int($value) && !is_float($value))
        {
            die('Допускаются только числовые, строковые, массивы и NULL аргументы в запросе. Аргумент '.($ctr+1).' является некорректным типом, его тип: '. gettype($value). '.');
        }
        $values[] = $value;
        $ctr++;
    }
    $query = preg_replace_callback(
        '/{(\\d+)}/', 
        function($match) use ($values)
        {
            if (isset($values[$match[1]]))
            {
                return $values[$match[1]];
            }
            else
            {
                return $match[0];
            }
        },
        $query
    );
    return $query;
}

function runEscapedQuery($preparedQuery /*, ...*/)
{
    $params = array_slice(func_get_args(), 1);
    $results = runQuery(sqlvprintf($preparedQuery, $params)); // Выполняем запрос и получаем результаты.   
    return $results;
}

Это позволяет выполнять запросы в одной строке, например:

runEscapedQuery("INSERT INTO Whatever (id, foo, bar) VALUES ({0}, {1}, {2})", $numericVar, $stringVar1, $stringVar2);

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

Важно отметить, что в предыдущей версии, использовавшей str_replace, была возможность инъекций за счет добавления токенов в пользовательские данные. Однако текущая версия с использованием preg_replace_callback не создает таких проблем, если в замещении присутствуют эти токены.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь