Эта проверка позволяет избежать SQL-инъекций? - PullRequest
0 голосов
/ 05 июля 2018

Мне нужно передать условие where от моего клиента в веб-сервис, который выполняет SQL-запрос к базе данных Oracle.

 const res = await db.execute(`SELECT count(ID) FROM table WHERE ${where}`);

Поскольку условие where может быть полностью динамическим, я не могу использовать подготовленные операторы, хранимую процедуру или параметры.

Достаточно ли просто выполнить условие where через следующую проверку?

static isDangerousSql(sql: string): boolean {
    const characters = [ '--', ';'];

    for (const char of characters) {
        if (sql.indexOf(char) > -1) {
            return true;
        }
    }

    return false;
}

1 Ответ

0 голосов
/ 05 июля 2018

Нет, этого недостаточно.

Если ${where}:

EXISTS (
  SELECT 1
  FROM   security_table
  WHERE  userid        = 1234
  AND    password_hash = CHR(65) || CHR(66) || CHR(67) || CHR(68)
)

Тогда будет:

  1. Передайте чек;
  2. Убедитесь, что существует таблица с именем security_table;
  3. Убедитесь, что в таблице есть столбцы userid и password_hash; и
  4. Подтвердите, что есть пользователь с идентификатором 1234 и хэшем пароля ABCD

Это может использоваться для отображения всей структуры вашей базы данных и для проверки существования любых данных. Определенно уязвимость.


Примером будет

WHERE status = 'Active' and ID in (SELECT cell_id FROM alerts WHERE alert_status = 1)

Вместо того, чтобы использовать динамический SQL, вы можете перечислить действительные условия фильтра в вашем запросе и использовать параметры связывания для заполнения условий фильтра:

Итак, более сложный пример:

SELECT count(ID)
FROM   table t
WHERE  ( :status IS NULL OR status = :status )
AND    (  ( :alert_status IS NULL AND :other_status IS NULL )
       OR EXISTS (
            SELECT 1
            FROM   alerts a
            WHERE  t.id = a.cell_id
            AND    ( :alert_status IS NULL OR alert_status = :alert_status )
            AND    ( :other_status IS NULL OR other_status = :other_status )
       ) )

Тогда у вас есть статический запрос, и вы можете передать (именованные) переменные связывания :status, :alert_status и :other_status.

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

Если вы не хотите иметь один статический запрос со всеми параметрами, тогда вы можете построить фильтры из фиксированных компонентов сниппета на среднем уровне.

Какой-то псевдокод:

sql             = "SELECT count(ID) FROM table";
filters         = [];
bind_parameters = [];
if ( [ 'Active', 'Inactive' ].indexOf( user_input.status ) > -1 )
{
  filters.push( "status = ?" )
  bind_parameters.push( user_input.status );
}
if ( user_input.active_status == 0 || user_input.active_status == 1 )
{
  filters.push( "id IN ( SELECT cell_id FROM alerts WHERE alert_status = ?)" );
  bind_parameters.push( user_input.active_status );
}
if ( filters.length > 0 )
{
  sql = sql + " WHERE " + filters.join( " AND " );
}
db.setSQL( sql );
for ( i = 0; i < bind_parameters.length; i++ )
{
  db.setBindParameter( i, bind_parameters[i] );
}
const res = await db.executeQuery();

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...