Os procedimentos armazenados impedem a injeção de SQL?

83

É verdade que os procedimentos armazenados impedem ataques de injeção de SQL nos bancos de dados PostgreSQL? Eu fiz uma pequena pesquisa e descobri que o SQL Server, Oracle e MySQL não são seguros contra injeção de SQL, mesmo se usarmos apenas procedimentos armazenados. No entanto, esse problema não existe no PostgreSQL.

A implementação do procedimento armazenado no núcleo do PostgreSQL evita ataques de injeção de SQL ou é outra coisa? Ou o PostgreSQL também é suscetível à injeção de SQL, mesmo se usarmos apenas procedimentos armazenados? Se sim, por favor, mostre-me um exemplo (por exemplo, livro, site, jornal, etc.).

Am1rr3zA
fonte
4
Estranhamente, as principais respostas aqui são principalmente o AT lidando com o SQL Server, enquanto a pergunta é sobre o Postgres . Aqui está uma resposta relacionada ao Postgres: dba.stackexchange.com/questions/49699/… . Existem alguns outros, tente uma pesquisa: dba.stackexchange.com/…
Erwin Brandstetter
@ErwinBrandstetter a pergunta original não foi marcada (pelo OP) com o postgres e foi - e ainda - menciona vários outros DBMS. Acho que esse é o motivo das várias respostas focadas em outros DBMS. Eu sugiro que você adicione mais um focado no Postgres.
ypercubeᵀᴹ
@ ypercubeᵀᴹ: adicionarei uma resposta aqui quando encontrar tempo. Enquanto isso, atualizei o dba.stackexchange.com/questions/49699/… para mais claro e abrangente.
Erwin Brandstetter

Respostas:

71

Não, os procedimentos armazenados não impedem a injeção de SQL. Aqui está um exemplo real (de um aplicativo interno criado por alguém onde eu trabalho) de um procedimento armazenado que infelizmente permite a injeção de SQL:

Este código do servidor sql:

CREATE PROCEDURE [dbo].[sp_colunmName2]   
    @columnName as nvarchar(30),
    @type as nvarchar(30), 
    @searchText as nvarchar(30)           
AS
BEGIN
    DECLARE @SQLStatement NVARCHAR(4000)
    BEGIN
        SELECT @SQLStatement = 'select * from Stations where ' 
            + @columnName + ' ' + @type + ' ' + '''' + @searchText + '''' 
        EXEC(@SQLStatement)
    END      
END
GO

aproximadamente equivalente ao postgres:

CREATE or replace FUNCTION public.sp_colunmName2 (
    columnName  varchar(30),
    type varchar(30), 
    searchText  varchar(30) ) RETURNS SETOF stations LANGUAGE plpgsql            
AS
$$
DECLARE SQLStatement VARCHAR(4000);
BEGIN
    SQLStatement = 'select * from Stations where ' 
            || columnName || ' ' || type || ' ' || ''''|| searchText || '''';
    RETURN QUERY EXECUTE  SQLStatement;
END
$$;

A idéia do desenvolvedor era criar um procedimento de pesquisa versátil, mas o resultado é que a cláusula WHERE pode conter tudo o que o usuário desejar, permitindo a visita de pequenas tabelas de Bobby .

Se você usa instruções SQL ou procedimento armazenado, não importa. O que importa é se o seu SQL usa parâmetros ou seqüências de caracteres concatenadas. Parâmetros impedem injeção de SQL; seqüências concatenadas permitem injeção de SQL.

Kyralessa
fonte
46

Os ataques de injeção de SQL são aqueles em que as entradas não confiáveis ​​são consultas diretamente anexadas, permitindo que o usuário efetivamente execute código arbitrário, conforme ilustrado neste quadrinho XKCD canônico.

Assim, entendemos a situação:

userInput = getFromHTML # "Robert ') Solta os alunos da tabela; -"

Consulta = "Selecione * dos alunos em que studentName =" + userInput

Os procedimentos armazenados são, em geral, boas defesas contra ataques de injeção SQL, porque os parâmetros recebidos nunca são analisados.

Em um procedimento armazenado, na maioria dos bancos de dados (e programas, não se esqueça que as consultas pré-compiladas contam como procedimentos armazenados) se parecem com o seguinte:

 

criar procedimento armazenado foo (
selecione * dos alunos em que studentName =: 1
);

Então, quando o programa deseja acessar, ele chama foo(userInput)e recupera o resultado com satisfação.

Um procedimento armazenado não é uma defesa mágica contra a injeção de SQL, pois as pessoas conseguem escrever procedimentos armazenados incorretos . No entanto, as consultas pré-compiladas, sejam elas armazenadas no banco de dados ou no programa, são muito mais difíceis de abrir brechas de segurança se você entender como o SQL-Injection funciona.

Você pode ler mais sobre SQL-Injection:

Brian Ballsun-Stanton
fonte
29

Sim, até certo ponto.
Somente os procedimentos armazenados não impedirão a injeção de SQL.

Deixe-me citar primeiro sobre a injeção de SQL da OWASP

Um ataque de injeção SQL consiste na inserção ou "injeção" de uma consulta SQL por meio dos dados de entrada do cliente para o aplicativo. Uma exploração bem-sucedida de injeção SQL pode ler dados confidenciais do banco de dados, modificar dados (Inserir / Atualizar / Excluir), executar operações de administração no banco de dados (como desligar o DBMS), recuperar o conteúdo de um determinado arquivo presente no arquivo DBMS sistema e, em alguns casos, emita comandos para o sistema operacional. Os ataques de injeção SQL são um tipo de ataque de injeção, no qual comandos SQL são injetados na entrada do plano de dados para efetuar a execução de comandos SQL predefinidos.

É necessário limpar as entradas do usuário e não concatenar instruções SQL, mesmo se você estiver usando o procedimento armazenado.

Jeff Attwood explicou as consequências da concatenação do sql em " Dê-me SQL parametrizado ou dê-me a morte "

A seguir, apresentamos o desenho interessante que sempre me ocorre quando ouço o SQL Injection texto alternativo , acho que você entendeu :-)

Dê uma olhada na Folha de Batalha de Prevenção de Injeção de SQL , os métodos de prevenção são bem explicados ...

CoderHawk
fonte
12

A concatenação de cadeias é a causa da injeção de SQL. Isso é evitado usando a parametrização.

Os procedimentos armazenados adicionam uma camada adicional de segurança ao impor sintaxe inválida ao concatenar, mas não são "mais seguros" se você usar, digamos, SQL dinâmico neles.

Portanto, seu código acima é causado pela concatenação dessas cadeias

  • exec sp_GetUser '
  • x' AND 1=(SELECT COUNT(*) FROM Client); --
  • ' , '
  • monkey
  • '

Isso fornece sintaxe inválida, felizmente

Parametrizar daria

exec sp_GetUser 'x'' AND 1=(SELECT COUNT(*) FROM Client); --' , 'monkey'

Isso significa

  • @UserName = x' AND 1=(SELECT COUNT(*) FROM Client); --
  • @Password = monkey

Agora, no código acima, você não terá linhas porque presumo que você não tem usuário x' AND 1=(SELECT COUNT(*) FROM Client); --

Se o processo armazenado tiver esta aparência (usando SQL dinâmico concatenado ), sua chamada de procedimento armazenado parametrizado ainda permitirá a injeção de SQL

...
SET @sql = 'SELECT userName from users where userName = ''' + 
               @UserName + 
               ''' and userPass = ''' +
               @Password +
               ''''
EXEC (@sql)
....

Portanto, como demonstrado, a concatenação de strings é o principal inimigo da injeção de SQL

Os procedimentos armazenados adicionam encapsulamento, manipulação de transações, permissões reduzidas etc., mas ainda podem ser abusados ​​pela injeção de SQL.

Você pode procurar no Stack Overflow para saber mais sobre parametrização

gbn
fonte
10

"Ataques de injeção SQL acontecer quando a entrada do usuário é indevidamente codificado. Normalmente, a entrada do usuário é alguns dados o usuário envia com sua consulta, valores ou seja, no $_GET, $_POST, $_COOKIE, $_REQUEST, ou $_SERVERmatrizes. No entanto, a entrada do usuário também pode vir de uma variedade de outros fontes, como soquetes, sites remotos, arquivos, etc. Portanto, você realmente deve tratar tudo, menos as constantes (como 'foobar') como entrada do usuário ".

Estive investigando minuciosamente esse assunto recentemente e gostaria de compartilhar com outras pessoas um material bastante interessante, tornando assim este post mais completo e instrutivo para todos.



Do YouTube


Da Wikipedia


De OWASP


Do manual do PHP


Da Microsoft e Oracle


Estouro de pilha


Scanner de injeção SQL

Ilia Rostovtsev
fonte
2

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

MWDB
fonte
Não se esqueça de mencionar quote_ident()- mas, em geral, a maneira mais fácil de escrever SQL dinâmico à prova de injeção é usar format()e usar os espaços reservados %Ipara identificadores e %Lliterais. Dessa forma, o SQL é muito mais legível do que a versão equivalente usando ||e quote_....()funções
a_horse_with_no_name