Os procedimentos armazenados não impedem magicamente a injeção de SQL, mas facilitam muito a prevenção. Tudo o que você precisa fazer é algo como o seguinte (exemplo do 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]
É isso aí! O problema só surge ao formar uma consulta por meio de concatenação de strings (ou seja, SQL dinâmico) e, mesmo nesses casos, você pode vincular! (Depende do banco de dados.)
Como evitar a injeção de SQL na sua consulta dinâmica:
Etapa 1) Pergunte a si mesmo se você realmente precisa de uma consulta dinâmica. Se você está juntando as cordas apenas para definir a entrada, provavelmente está fazendo algo errado. (Existem exceções a essa regra - uma exceção é para relatar consultas em alguns bancos de dados; você pode ter problemas de desempenho se não forçar a compilação de uma nova consulta a cada execução. Mas pesquise esse problema antes de pular para isso. )
Etapa 2) Pesquise a maneira correta de definir a variável para seu RDBMS específico. Por exemplo, o Oracle permite que você faça o seguinte (citando seus documentos):
sql_stmt := 'UPDATE employees SET salary = salary + :1 WHERE '
|| v_column || ' = :2';
EXECUTE IMMEDIATE sql_stmt USING amount, column_value; --INJECTION IMMUNE!!
Aqui você ainda não está concatenando a entrada. Você está vinculando com segurança! Viva!
Se seu banco de dados não suportar algo como o descrito acima (espero que nenhum deles ainda seja tão ruim, mas não ficaria surpreso) - ou se você ainda precisar concatenar sua entrada (como no caso "às vezes" de relatar consultas como Eu sugeri acima), então você deve usar uma função de escape adequada. Não escreva você mesmo. Por exemplo, o postgres fornece a função quote_literal (). Então você executaria:
sql_stmt := 'SELECT salary FROM employees WHERE name = ' || quote_literal(in_name);
Dessa forma, se in_name for algo desonesto como '[snip] ou 1 = 1' (a parte "ou 1 = 1" significa selecionar todas as linhas, permitindo que o usuário veja salários que ele não deveria!), Então quote_literal salva sua bunda por fazendo a sequência resultante:
SELECT salary FROM employees WHERE name = '[snip] or 1=1'
Nenhum resultado será encontrado (a menos que você tenha alguns funcionários com nomes realmente estranhos).
Essa é a essência disso! Agora, deixe-me deixar um link para um post clássico do guru do Oracle, Tom Kyte, sobre o SQL Injection, para esclarecer o assunto: Linky