Как предотвратить внедрение SQL при динамическом генерировании DDL? - PullRequest
0 голосов
/ 01 января 2019

Цель: динамически генерировать PreparedStatement невосприимчивый к SQL-инъекции.

    // This is a bad method. SQL injection danger . But it works 
    private PreparedStatement generateSQLBad(Connection connection, String tableName, 
        String columnName, String columnType) throws SQLException {
        String sql = "create table " + tableName + " (" + columnName + " " + columnType + ")";
        PreparedStatement create = connection.prepareStatement(sql);
        return create;
    }

    // I tried this. But it didn't work  
    private PreparedStatement generateSQLGood(Connection connection, String tableName, 
        String columnName, String columnType) throws SQLException {
        String sql = "create table ? (? ?)";
        PreparedStatement create = connection.prepareStatement(sql);
        create.setString(1, tableName);
        create.setString(2, columnName);
        create.setString(3, columnType);
        return create;
    } 

Как динамически генерировать PreparedStatement, где пользователь может выбрать имя таблицы, тип столбца и т. Д., И нет опасности внедрения SQL-кода?

1 Ответ

0 голосов
/ 01 января 2019

Нельзя использовать ? заполнителей параметров для идентификаторов (имен таблиц и столбцов).Они также не могут использоваться для ключевых слов SQL, таких как типы данных.Подготовка запроса должна иметь возможность проверить синтаксис и убедиться, что имена таблиц и т. Д. Являются допустимыми.Это должно быть сделано во время подготовки, а не во время выполнения.SQL не позволяет параметрам содержать синтаксис.Они всегда рассматриваются как скалярные значения. Вот как они защищают от внедрения SQL.

Таким образом, параметры можно использовать только вместо скалярных литералов, таких как строки или даты в кавычках или числовые значения.

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

Все реализации SQL позволяют использовать специальные символы в именах таблиц, если вы разделяете идентификатор.Стандартный SQL использует двойные кавычки для разделителей.MySQL использует обратные галочки, а Microsoft SQL Server использует квадратные скобки.

Дело в том, что вы можете создавать странные имена таблиц таким образом, как имена таблиц, содержащие пробелы, или знаки препинания, или международные символы, или SQLзарезервированные слова

CREATE TABLE "my table" ( col1 VARCHAR(20) );

CREATE TABLE "order" ( col1 VARCHAR(20) );

См. Также мой ответ на https://stackoverflow.com/a/214344/20860

Но что, если само имя таблицы содержит буквенный символ двойной кавычки?Тогда вы должны избежать этого персонажа.Либо используйте двойной символ, либо обратную косую черту:

CREATE TABLE "Dwayne ""The Rock"" Johnson" ( col1 VARCHAR(20) );

CREATE TABLE "Dwayne \"The Rock\" Johnson" ( col1 VARCHAR(20) );

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

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

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

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

...