flawopen.com/Injeção de SQL/Procedimentos armazenados
Procedimentos armazenados evitam SQL injection?
Não — isso é um equívoco comum. Um procedimento armazenado protege você apenas se usar vinculação de parâmetros internamente. Se montar a consulta concatenando entradas em uma string e executá-la com EXEC ou sp_executesql, o risco é o mesmo da concatenação na aplicação.
-- SQL Server T-SQL; dbo.Users.Id is NVARCHAR(50).
CREATE PROCEDURE dbo.GetUser
@UserId NVARCHAR(50)
AS
BEGIN
DECLARE @sql NVARCHAR(MAX) =
N'SELECT Id FROM dbo.Users WHERE Id = N'''
+ @UserId + N'''';
EXEC(@sql);
END;
-- SQL Server T-SQL; same illustrative schema.
CREATE PROCEDURE dbo.GetUser
@UserId NVARCHAR(50)
AS
BEGIN
DECLARE @sql NVARCHAR(MAX) =
N'SELECT Id FROM dbo.Users WHERE Id = @id';
EXEC sys.sp_executesql
@stmt = @sql,
@params = N'@id NVARCHAR(50)',
@id = @UserId;
END;
Por que esse equívoco é comum
“Use procedimentos armazenados” costuma ser apresentado como solução geral para SQL injection. Isso funciona quando o procedimento passa valores como parâmetros vinculados em uma consulta normal. Se construir SQL dinâmico com EXEC e concatenar valores não confiáveis, recria o mesmo risco. Quando SQL dinâmico for necessário, vincule os valores com sp_executesql. Nomes de tabelas ou colunas não podem ser vinculados como valores; mapeie as opções permitidas para identificadores fixos por meio de uma allowlist.
Perguntas frequentes
Sim. sp_executesql aceita parâmetros: declare-os e passe os valores separadamente (por exemplo, EXEC sp_executesql @sql, N'@id INT', @id = @UserId). Não concatene valores em @sql. Para nomes dinâmicos de tabela ou coluna, use uma allowlist estrita, pois identificadores não são parâmetros vinculáveis.