Избежать инъекции SQL при вставке сочетания жестко запрограммированных и переменных значений?

Я пишу запросы к базе данных с помощью pg-promise. Мои таблицы выглядят так:

                                    Table "public.setting"
│ user_id          │ integer           │ not null                
│ visualisation_id │ integer           │ not null  
│ name             │ character varying │ not null                                  

                                    Table "public.visualisation"
│ visualisation_id │ integer           │ not null                
│ template_id      │ integer           │ not null  

Я хочу вставить несколько значений в setting — три из них жестко закодированы, а одно мне нужно найти в visualisation.

Следующий оператор делает то, что мне нужно, но должен быть уязвим для SQL-инъекций:

var q = "INSERT INTO setting (user_id, visualisation_id, template_id) (" +
        "SELECT $1, $2, template_id, $3 FROM visualisation WHERE id = $2)";
conn.query(q, [2, 54, 'foo']).then(data => {
    console.log(data); 
});

Я знаю, что должен использовать имена SQL, но если я попробуйте использовать их следующим образом, я получаю TypeError: Invalid sql name: 2:

var q = "INSERT INTO setting (user_id, visualisation_id, template_id) (" +
        "SELECT $1~, $2~, template_id, $3~ FROM visualisation WHERE id = $2)";

что, я думаю, неудивительно, поскольку 2 заключено в двойные кавычки, поэтому SQL считает, что это имя столбца.

Если я попытаюсь переписать запрос, чтобы использовать VALUES, я также получу синтаксическую ошибку:

var q = "INSERT INTO setting (user_id, visualisation_id, template_id) VALUES (" +
        "$1, $2, SELECT template_id FROM visualisation WHERE id = $2, $3)";

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


person Richard    schedule 24.08.2017    source источник
comment
чтобы использовать запрос как значение, возьмите его в скобки, как подзапрос, например "$1, $2, (SELECT template_id FROM visualisation WHERE id = $2), $3)"   -  person Vao Tsun    schedule 24.08.2017
comment
Я думаю, что @VaoTsun здесь прав. И имена SQL здесь не при чем, поскольку вы не используете динамические имена столбцов.   -  person vitaly-t    schedule 24.08.2017
comment
Спасибо обоим. В первом примере, не может ли пользователь указать какой-то SQL вместо $1 и потенциально осуществить SQL-инъекцию, потому что внутренний запрос использует динамические имена столбцов? (Я проверяю существующий код на наличие уязвимостей.)   -  person Richard    schedule 24.08.2017
comment
@ Ричард Я так не думаю, поскольку $1 представляет собой простое значение. Если вы передадите ему любую строку, она будет правильно экранирована как строка, инъекция будет невозможна.   -  person vitaly-t    schedule 24.08.2017
comment
Интересно, спасибо! Значит, в таком запросе, как SELECT $1, $2, template_id, $3 FROM visualisation WHERE id = $2, нет необходимости использовать имена SQL? Зачем тогда они вообще нужны?   -  person Richard    schedule 24.08.2017
comment
@Richard, вам нужно будет использовать имена SQL там, в простом SELECT. Но ваш пример — это INSERT, использующий SELECT, а это совсем другая история.   -  person vitaly-t    schedule 24.08.2017


Ответы (1)


Ваш запрос в порядке. Я думаю, вы также знаете заполнители значений (параметр $X) и имена SQL, но вы немного запутались.

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

В документации говорится:

Когда тип данных параметра не указан или объявлен как неизвестный, тип выводится из контекста, в котором используется параметр (если возможно).

Я не могу найти источник, в котором указывается тип по умолчанию, но я думаю, что оператор INSERT предоставляет достаточный контекст для определения реальных типов.

С другой стороны, вы должны использовать имена SQL, запрашивать динамически. Например, у вас есть переменные имена столбцов или таблиц. Они должны быть вставлены через параметры стиля $1~ или $1:name, чтобы защитить вас от атак внедрения.

person Laposhasú Acsa    schedule 24.08.2017