Хранимые процедуры магическим образом не предотвращают внедрение SQL, но они значительно упрощают его предотвращение. Все, что вам нужно сделать, это что-то вроде следующего (пример Postgres):
CREATE OR REPLACE FUNCTION my_func (
IN in_user_id INT
)
[snip]
SELECT user_id, name, address FROM my_table WHERE user_id = in_user_id; --BAM! SQL INJECTION IMMUNE!!
[snip]
Это оно! Проблема возникает только при формировании запроса с помощью конкатенации строк (например, динамический SQL), и даже в этих случаях вы можете связать! (Зависит от базы данных.)
Как избежать внедрения SQL в ваш динамический запрос:
Шаг 1) Спросите себя, действительно ли вам нужен динамический запрос. Если вы соединяете строки только для того, чтобы установить ввод, то вы, вероятно, делаете это неправильно. (Из этого правила есть исключения - одно исключение относится к отчетам о запросах в некоторых базах данных. У вас могут быть проблемы с производительностью, если вы не заставляете его скомпилировать новый запрос при каждом выполнении. Но изучите эту проблему, прежде чем переходить к этому. )
Шаг 2) Изучите правильный способ установки переменной для вашей конкретной СУБД. Например, Oracle позволяет вам делать следующее (цитируя их документы):
sql_stmt := 'UPDATE employees SET salary = salary + :1 WHERE '
|| v_column || ' = :2';
EXECUTE IMMEDIATE sql_stmt USING amount, column_value; --INJECTION IMMUNE!!
Здесь вы все еще не объединяете входные данные. Вы в безопасности! Ура!
Если ваша база данных не поддерживает что-то подобное выше (надеюсь, что ни один из них все еще не настолько плох, но я не удивлюсь) - или если вам все еще действительно нужно объединить свои данные (как в «иногда» случае представления запросов как Я намекал выше), тогда вы должны использовать правильную функцию выхода. Не пиши это сам. Например, postgres предоставляет функцию quote_literal (). Таким образом, вы бы запустить:
sql_stmt := 'SELECT salary FROM employees WHERE name = ' || quote_literal(in_name);
Таким образом, если in_name является чем-то коварным, как '[snip] или 1 = 1' (часть "или 1 = 1" означает выбрать все строки, позволяя пользователю видеть зарплаты, которые он не должен!), То quote_literal сохраняет ваш зад сделать результирующую строку:
SELECT salary FROM employees WHERE name = '[snip] or 1=1'
Результаты не будут найдены (если у вас нет сотрудников с действительно странными именами.)
В этом суть! Теперь позвольте мне оставить вам ссылку на классическую статью гуру Oracle Тома Кайта на тему SQL-инъекций, чтобы понять суть : Linky