SQL Инъекционно-безопасный вызов полиморфной функции
Несколько раз я нашел рефакторинг кода веб-приложения и в конечном итоге хочу сделать что-то вроде этого (Groovy в этом случае, но может быть что угодно):
Map getData(String relationName, Integer rowId) {
def sql = Sql.newInstance([...])
def result = sql.firstRow('SELECT getRelationRow(?,?)', relationName, rowId)
sql.close()
return new HashMap(result)
}
где хранимая процедура getRelationRow(relname text, rowid integer)
выполняет динамический sql для извлечения строки указанного rowid
в запрошенном отношении. Лучшим примером, который я видел для такой функции, является эта полиморфная функция с использованием типа anyelement
и называется
SELECT * FROM data_of(NULL::pcdmet, 17);
Однако для вызова этого в приведенном выше коде потребуется
def result = sql.firstRow("SELECT * FROM data_of(NULL::${relationName},?)", rowId)
то есть потребовалось, чтобы имя отношения вставлялось в запрос, что угрожает SQL Injection. Итак, есть ли возможность сохранить полиморфную доброту хранимой процедуры, но разрешить ее вызывать с именами общих имен?
Ответы
Ответ 1
Я не думаю, что это можно сделать так. Я предполагаю, что Groovy использует подготовленные инструкции здесь, что требует, чтобы типы ввода и возврата были известны во время подготовки, тогда как моя функция выводит тип возвращаемого значения из типа полиморфного ввода.
Я уверен, вам нужна конкатенация строк. Но не волнуйтесь, есть функции типа pg_escape()
для дезинфекции имен таблиц и невозможности SQLi. Не знаю Groovy, но это тоже должно быть.
Или это?
В зависимости от функции data_of(..)
в конце этого связанного ответа:
С PREPARE
Я могу сделать эту работу, явно объявив тип возвращаемого значения:
PREPARE fooplan ("relationName") AS -- table name works as row type
SELECT * FROM data_of($1, $2);
Тогда я могу передать NULL
, который передается в "relationName"
из подготовленного контекста:
EXECUTE fooplan(NULL, 1);
Таким образом, это может работать в конце концов, если ваш интерфейс поддерживает это. Но вы все равно должны объединить имя таблицы в качестве типа возвращаемых данных (и, следовательно, защититься от SQLi). Поймать 22, я думаю.