Предотвращение введения SQL в классе базы данных

Я создаю класс базы данных и думал, что это будет хорошая идея включить некоторую форму предотвращения Внедрения SQL (понятное дело!). Вот метод, который выполняет запрос базы данных:

class DB
{
    var $db_host    = 'localhost';
    var $db_user    = 'root';
    var $db_passwd  = '';
    var $db_name    = 'whatever';

    function query($sql)
    {
        $this->result = mysql_query($sql, $this->link);
        if(!$this->result)
        {
           $this->error(mysql_error());
        } else {
            return $this->result;
        }
    }
}

Существует больше в классе, чем это, но я сокращаю его только для этого. Проблема, с которой я сталкиваюсь, состоит в том, если я просто использую mysql_real_escape_string($sql, $this->link); затем это выходит из всего запроса и приводит к ошибке синтаксиса SQL. Как я могу динамично найти переменные, которых нужно оставить? Я хочу избегать использования mysql_real_escape_string() в моих основных блоках кода у меня был бы он в функции.

Спасибо.

5
задан rook 29 March 2010 в 21:28
поделиться

8 ответов

Либо параметризуйте, либо попробуйте создать класс БД, чтобы передать все значения для WHERE (и все, что может использовать значения), используя что-то вроде:

$db->where(x,y);

т.е.

$db->where('userid','22');

И в корпусе классов используйте что-то вроде

function where(var x, var y) // method
{
    $this->where .= x . ' = '.mysql_real_escape_string(y);
}

Конечно, это требует очистки для поддержки нескольких входов WHERE.

1
ответ дан 15 December 2019 в 00:56
поделиться

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

Или если вы не разрешаете пользователям передавать SQL в метод query (). Это слишком низкий уровень для реализации экранирующих параметров. Как вы поняли, вам нужно экранировать только параметры, а не все операторы SQL.

Если вы параметризуете SQL, вы можете избежать только параметров. В этом случае я предполагаю, что пользователи могут влиять на значения параметров, а не на SQL.

Взгляните на методы PDO bindParam () или Zend_DB query () , чтобы понять, как это реализовано в других интерфейсах баз данных.

0
ответ дан 15 December 2019 в 00:56
поделиться

Зачем изобретать велосипед? Просто расширите PDO и используйте параметризованные запросы. Если вы не знаете, что это такое, прочтите документ, который содержит множество примеров, чтобы начать работу.

0
ответ дан 15 December 2019 в 00:56
поделиться

Проблема в том, что к тому времени, когда вы построите SQL-запрос, уже слишком поздно, чтобы можно было предотвратить инъекцию путем поиска переменных - иначе это уже было бы быть встроенным в PHP.

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

Однако я бы порекомендовал другой подход - наличие уровня, который предоставляет таблицу базы данных в качестве объекта. Вот пример пользовательского объекта , который является производным от базового класса сущности базы данных. , который обеспечивает полный интерфейс к базе данных с использованием шаблона активной записи и шаблона итератора .

Я проиллюстрирую это на нескольких примерах; Самое интересное здесь - итераторы, поскольку вы можете абстрагироваться от гораздо большего и иметь несколько довольно общих классов в дальнейшем для извлечения данных.

Чтобы создать запись пользователя, используя описанный выше подход:

$user = new DbUser();
$user->create();
$user->set_email('test@example.com');
$user->write();

Чтобы прочитать запись пользователя:

$user = new DbUser();
$user->set_email('text@example.com');
if ($user->load_from_fields())
{
}

Чтобы перебрать записи:

$user_iterator = DbUser::begin();
if ($user_iterator->begin())
{
    do
    {
         $user = $user_iterator->current();
         echo $user->get_email();
    } while ($user_iterator->next());
}
1
ответ дан 15 December 2019 в 00:56
поделиться

Чтобы сделать это правильно, вы должны дезинфицировать материал при построении запроса или поместить его в свой класс, чтобы передавать параметры независимо основного запроса.

0
ответ дан 15 December 2019 в 00:56
поделиться

Я решил эту проблему, добавив параметры в функцию запроса. Я обнаружил, что codeigniter делает это довольно красиво, поэтому я адаптировал его под свои вкусы.

Пример:

$result = Database::query('INSERT INTO table (column1,column2,column3) VALUES(?,?,?)',array($value1,$value2,$value3));



public static $bind_marker = '?';
public static function query($query, $binds = FALSE)
    {
        if($binds !== FALSE)
        {
            $query = self::compile_binds($query,$binds);
        }
        // $query now should be safe to execute
}

private static function compile_binds($query, $binds)
    {
        if(strpos($query, self::$bind_marker) === FALSE)
        {
            return $query;
        }

        if(!is_array($binds))
        {
            $binds = array($binds);
        }

        $segments = explode(self::$bind_marker, $query);

        if(count($binds) >= count($segments))
        {
            $binds = array_slice($binds, 0, count($segments)-1);
        }

        $result = $segments[0];
        $i = 0;
        foreach($binds as $bind)
        {
            if(is_array($bind))
            {
                $bind = self::sanitize($bind);
                $result .= implode(',',$bind);
            }
            else
            {
                $result .= self::sanitize($bind);
            }

            $result .= $segments[++$i];
        }

        return $result;
    }

public static function sanitize($variable)
{
    if(is_array($variable))
    {
        foreach($variable as &$value)
        {
            $value = self::sanitize($value);
        }
    }
    elseif(is_string($variable))
    {
        mysql_real_escape_string($variable);
    }
    return $variable;
}

Главное дополнение, которое я добавил из версии Codeigniter, это то, что я могу использовать массив в качестве параметра, что полезно для использования "IN":

$parameters = array
    (
        'admin',
        array(1,2,3,4,5)
    );

$result = Database::query("SELECT * FROM table WHERE account_type = ? AND account_id IN (?)",$parameters);
0
ответ дан 15 December 2019 в 00:56
поделиться

Весь смысл предотвращения внедрения кода заключается в том, что вы хотите различать свой sql и sql, который внедряли пользователи. Вы больше не можете делать это на этом самом низком уровне. Приведем пример:

select * from users where username='test' and password='itisme' or '4'='4'

Это кажется идеальным допустимым sql, но это также может быть версия sql injected:

"select * from users where username='test' and password='" . "itisme' or '4'='4". "'"

Так что вы должны сделать это дальше в своем коде или использовать оболочки, как предлагали другие.

1
ответ дан 15 December 2019 в 00:56
поделиться

Существует два подхода к предотвращению атаки с использованием SQL-инъекции: подход на основе черного списка и подход на основе белого списка.

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

Попробуйте использовать параметризованный SQL в своем коде, это один из глобально адаптированных принципов безопасного кодирования.

1
ответ дан 15 December 2019 в 00:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: