Por que o mecanismo de prevenção de injeção de SQL evoluiu na direção do uso de consultas parametrizadas?


59

Do meu ponto de vista, os ataques de injeção de SQL podem ser evitados por:

  1. Triagem cuidadosa, filtragem, entrada de codificação (antes da inserção no SQL)
  2. Usando instruções preparadas / consultas parametrizadas

Suponho que existem prós e contras para cada um, mas por que o nº 2 decolou e se tornou considerado mais ou menos a maneira de fato de impedir ataques de injeção? É apenas mais seguro e menos propenso a erros ou houve outros fatores?

Pelo que entendi, se o item 1 for usado corretamente e todas as advertências forem atendidas, ele poderá ser tão eficaz quanto o item 2.

Higienização, filtragem e codificação

Houve alguma confusão da minha parte entre o que significa higienizar , filtrar e codificar . Eu direi que, para meus propósitos, todas as opções acima podem ser consideradas para a opção 1. Nesse caso, eu entendo que a limpeza e a filtragem têm o potencial de modificar ou descartar dados de entrada, enquanto a codificação preserva os dados como estão , mas os codifica. corretamente para evitar ataques de injeção. Acredito que a fuga de dados pode ser considerada uma forma de codificá-los.

Consultas com parâmetros versus biblioteca de codificação

Existem respostas onde conceitos parameterized queriese encoding librariesque são tratados de forma intercambiável. Corrija-me se estiver errado, mas tenho a impressão de que são diferentes.

Meu entendimento é que encoding libraries, por melhores que sejam, sempre têm o potencial de modificar o "Programa" do SQL, porque estão fazendo alterações no próprio SQL, antes de ser enviado ao RDBMS.

Parameterized queries por outro lado, envie o programa SQL para o RDBMS, que otimiza a consulta, define o plano de execução da consulta, seleciona índices a serem usados ​​etc., e depois conecta os dados, como a última etapa dentro do RDBMS em si.

Biblioteca de codificação

  data -> (encoding library)
                  |
                  v
SQL -> (SQL + encoded data) -> RDBMS (execution plan defined) -> execute statement

Consulta parametrizada

                                               data
                                                 |
                                                 v
SQL -> RDBMS (query execution plan defined) -> data -> execute statement

Importância histórica

Algumas respostas mencionam que, historicamente, as consultas parametrizadas (PQ) foram criadas por motivos de desempenho e antes que os ataques de injeção direcionados a problemas de codificação se tornassem populares. Em algum momento, ficou claro que o PQ também era bastante eficaz contra ataques de injeção. Para manter o espírito da minha pergunta, por que o PQ permaneceu o método de escolha e por que floresceu acima da maioria dos outros métodos quando se trata de impedir ataques de injeção de SQL?


11
Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
maple_shaft

23
As instruções preparadas não são resultado da evolução dos ataques de injeção de SQL. Eles estavam lá desde o começo. Sua pergunta é baseada em uma premissa falsa.
user207421

4
Se você pensa que é mais esperto do que os bandidos, em seguida, ir para # 1
paparazzo

11
"por que o PQ permaneceu o método de escolha" Porque é o mais fácil e mais robusto. Além das vantagens de desempenho acima mencionadas para os PQs. Realmente não há uma desvantagem.
Paul Draper

11
Porque é a solução correta para o problema de como fazer consultas, mesmo que não fosse o problema da injeção de SQL em um contexto de segurança . Os formulários que exigem escape e usam dados em banda com comandos são sempre um bug de design, pois são propensos a erros, contra-intuitivos e quebram muito quando usados ​​incorretamente. Veja também: script de shell.
R ..

Respostas:


147

O problema é que o nº 1 exige que você efetivamente analise e interprete a totalidade da variante SQL em que está trabalhando, para saber se está fazendo algo que não deveria. E mantenha esse código atualizado enquanto atualiza seu banco de dados. Em todos os lugares você aceita entradas para suas consultas. E não estragar tudo.

Então, sim, esse tipo de coisa interromperia os ataques de injeção de SQL, mas é absurdamente mais caro de implementar.


60
@ dennis - Bem, o que é uma citação na sua variante SQL? U + 2018 \ u2018 Existem truques para separar expressões podem seus subqueries fazer alterações há " '?“???? Muitas coisas a considerar.
Telastyn

7
@Dennis, todo mecanismo de banco de dados tem sua própria maneira de fazer coisas como escapar de caracteres em strings. Existem muitos buracos a serem resolvidos, especialmente se um aplicativo precisar trabalhar com vários mecanismos de banco de dados ou ser compatível com versões futuras do mesmo mecanismo que possam alterar algumas sintaxes de consulta menores que possam ser exploradas.

12
Outro benefício das instruções preparadas é o ganho de desempenho que você obtém quando precisa executar novamente a mesma consulta, com valores diferentes. Além disso, as instruções preparadas podem saber se um valor realmente é entendido como nulluma sequência ou número e agir de acordo. Isso é muito bom para segurança. E mesmo se você executar a consulta uma vez, o mecanismo do banco de dados já a otimizará. Melhor ainda se estiver em cache!
Ismael Miguel

8
@Dennis Mr. Henry Null, obrigado por fazer isso da maneira certa.
Mathieu Guindon

14
@ Dennis, o primeiro nome é irrelevante. O problema está no sobrenome. Veja Stack Overflow , Programmers.SE , Fox Sports , Wired , BBC , e qualquer outra coisa que você pode transformar-se em uma rápida pesquisa no Google ;-)
Mathieu Guindon

80

Porque a opção 1 não é uma solução. Triagem e filtragem significa rejeitar ou remover entrada inválida. Mas qualquer entrada pode ser válida. Por exemplo, apóstrofo é um caractere válido no nome "O'Malley". Só precisa ser codificado corretamente antes de ser usado no SQL, que é o que as instruções preparadas fazem.


Depois de adicionar a nota, parece que você está basicamente perguntando por que usar uma função de biblioteca padrão em vez de escrever seu próprio código funcionalmente semelhante do zero? Você sempre deve preferir soluções de biblioteca padrão a escrever seu próprio código. É menos trabalho e mais sustentável. Esse é o caso de qualquer funcionalidade, mas especialmente para algo que é sensível à segurança, não faz absolutamente sentido reinventar a roda por conta própria.


2
É isso (e essa foi a parte que faltava em duas outras respostas, então +1). Dada a forma como a pergunta é formulada, não se trata de higienizar a entrada do usuário, mas cito a pergunta: “filtrando a entrada (antes da inserção)”. Se a questão agora é desinfetar a entrada, por que você faria isso em vez de deixar a biblioteca fazer isso (além de perder a oportunidade de ter planos de execução em cache, a propósito)?
Arseni Mourzenko

8
@ Dennis: higienizar ou filtrar significa remover informações. Codificar significa transformar a representação dos dados sem perder informações.
precisa saber é o seguinte

9
@Dennis: filtrar significa aceitar ou rejeitar a entrada do usuário. Por exemplo, "Jeff" seria filtrado como entrada do campo "Idade do usuário", porque o valor é obviamente inválido. Se, em vez de filtrar a entrada, você começar a transformá-la, substituindo, por exemplo, o caractere de aspas simples, estará fazendo exatamente o mesmo que as bibliotecas de banco de dados nas quais elas usam consultas parametrizadas; neste caso, a sua pergunta é simplesmente “Por que eu iria usar algo que existe e foi escrito por especialistas na área, quando eu posso reinventar a roda em cada projeto?”
Arseni Mourzenko

3
@ Dennis: O\'Malleyestá usando a barra para escapar da cotação para inserção adequada (pelo menos em alguns bancos de dados). No MS SQL ou no Access, ele pode ser escapado com uma cotação adicional O''Malley. Não é muito portátil se você tiver que fazer isso sozinho.
AbraCadaver

5
Não sei dizer quantas vezes meu nome foi totalmente rejeitado por um sistema. Às vezes, eu até vi erros causados ​​pela injeção de SQL apenas por usar meu nome. Heck, me pediram uma vez para alterar meu nome de usuário porque eu estava realmente quebrando algo no back-end.
Alexander O'Mara

60

Se você está tentando fazer o processamento de strings, não está realmente gerando uma consulta SQL. Você está gerando uma sequência que pode produzir uma consulta SQL. Há um nível de indireção que abre muito espaço para erros e bugs. É realmente um tanto surpreendente, dado que na maioria dos contextos estamos felizes em interagir com algo programaticamente. Por exemplo, se temos alguma estrutura de lista e queremos adicionar um item, geralmente não fazemos:

List<Integer> list = /* a list of 1, 2, 3 */
String strList = list.toString();   /* to get "[1, 2, 3]" */
strList = /* manipulate strList to become "[1, 2, 5, 3]" */
list = parseList(strList);

Se alguém sugerir isso, você responderia com razão que é bastante ridículo e que isso deve ser feito:

List<Integer> list = /* ... */;
list.add(5, position=2);

Isso interage com a estrutura de dados em seu nível conceitual. Ele não introduz nenhuma dependência de como essa estrutura pode ser impressa ou analisada. Essas são decisões completamente ortogonais.

Sua primeira abordagem é como a primeira amostra (apenas um pouco pior): você está assumindo que pode programaticamente construir a string que será analisada corretamente como a consulta que você deseja. Isso depende do analisador e de um monte de lógica de processamento de strings.

A segunda abordagem do uso de consultas preparadas é muito mais parecida com a segunda amostra. Ao usar uma consulta preparada, você essencialmente analisa uma pseudo-consulta legal, mas possui alguns espaços reservados e, em seguida, usa uma API para substituir corretamente alguns valores. Você não envolve mais o processo de análise e não precisa se preocupar com nenhum processamento de string.

Em geral, é muito mais fácil e muito menos propenso a erros interagir com as coisas em seu nível conceitual. Uma consulta não é uma sequência, é o que você obtém quando analisa uma sequência ou constrói uma programaticamente (ou qualquer outro método que permita criar uma).

Há uma boa analogia aqui entre macros no estilo C, que substituem texto simples, e macros no estilo Lisp, que geram arbitrariamente códigos. Com macros de estilo C, você pode substituir o texto no código-fonte, o que significa que você pode introduzir erros sintáticos ou comportamento enganoso. Com as macros Lisp, você está gerando código da forma que o compilador a processa (ou seja, você está retornando as estruturas de dados reais que o compilador processa, não o texto que o leitor precisa processar antes que o compilador possa acessá-lo) . Com uma macro Lisp, você não pode gerar algo que seria um erro de análise. Por exemplo, você não pode gerar (deixe ((ab) a .

Mesmo com as macros Lisp, você ainda pode gerar um código incorreto, porque não precisa necessariamente conhecer a estrutura que deveria estar lá. Por exemplo, em Lisp, (let ((ab)) a) significa "estabelecer uma nova ligação lexical da variável a ao valor da variável b e, em seguida, retorne o valor de a" e (let (ab) a) significa "estabeleça novas ligações lexicais das variáveis ​​aeb e inicialize as duas para zero e, em seguida, retorne o valor de a." Ambos são sintaticamente corretos, mas significam coisas diferentes. Para evitar esse problema, você pode usar mais funções de reconhecimento semântico e fazer algo como:

Variable a = new Variable("a");
Variable b = new Variable("b");
Let let = new Let();
let.getBindings().add(new LetBinding(a,b));
let.setBody(a);
return let;

Com algo assim, é impossível retornar algo sintaticamente inválido e é muito mais difícil retornar algo que acidentalmente não é o que você queria.


Boa explicação!
Mike Partridge

2
Você me perdeu na "boa analogia", mas eu votei com base na explicação anterior. :)
Curinga

11
Excelente exemplo! - E você pode adicionar: Dependendo do tipo de dados, às vezes nem é possível nem possível criar uma sequência analisável. - E se um dos meus parâmetros for um campo de texto livre contendo um rascunho de história (~ 10.000 caracteres)? ou e se um parâmetro for uma imagem JPG ? - A única maneira, então, é uma consulta parametrizada
Falco

Na verdade não - é uma descrição muito ruim de por que as instruções preparadas evoluíram como uma defesa para a injeção de sql. Particularmente, o exemplo de código está no java, que não existia quando as consultas parametrizadas se desenvolveram provavelmente no período em que o C / C ++ era considerado o estado da arte. Os bancos de dados SQL começaram a ser usados ​​nos primeiros anos do período 1970-1980. Muito antes de idiomas de nível superior, onde populares. Heck, eu diria que muitos deles vieram para tornar o trabalho com bases de dados mais fácil (PowerBuilder alguém?)
TomTom

@ TomTom, na verdade, concordo com a maior parte do seu conteúdo. Apenas toquei implicitamente no aspecto de segurança aqui. No SO, respondo a muitas perguntas sobre SPARQL (a linguagem de consulta RDF, com algumas semelhanças com SQL) e muitas pessoas enfrentam problemas porque concatenam seqüências de caracteres em vez de usar consultas parametrizadas. Mesmo sem ataques de injeção, consultas parametrizadas ajudam a evitar bugs / falhas, e bugs / falhas também podem ser problemas de segurança, mesmo que não sejam ataques de injeção. Então eu diria que menos e mais: consultas parametrizadas são boas, mesmo se a injeção de SQL não foi um problema, e eles são bons ...
Joshua Taylor

21

Ajuda que a opção 2 seja geralmente considerada uma prática recomendada, porque o banco de dados pode armazenar em cache a versão não parametrizada da consulta. Consultas parametrizadas antecedem a questão da injeção de SQL por vários anos (eu acredito), acontece que você pode matar dois coelhos com uma cajadada só.


10
A injeção de SQL tem sido um problema desde que o SQL foi inventado. Não se tornou um problema mais tarde.
Servy

9
@Servy Teoricamente sim. Praticamente, isso se tornou um problema real quando nossos mecanismos de entrada entraram em operação, apresentando uma superfície de ataque massiva para qualquer pessoa.
Jan Doggen

8
Little Bobby Tables discordaria que você precisa da Internet nem de uma grande base de usuários para alavancar a injeção de SQL. E, é claro, as redes são anteriores ao SQL, portanto, não é necessário esperar pelas redes quando o SQL for lançado. Sim, as vulnerabilidades de segurança são menos vulneráveis ​​quando seu aplicativo tem uma pequena base de usuários, mas ainda são vulnerabilidades de segurança, e as pessoas as exploram quando o próprio banco de dados possui dados valiosos (e muitos bancos de dados muito antigos tinham dados muito valiosos, pois somente as pessoas com bancos de dados valiosos podia pagar a tecnologia) ..
Servy

5
@ Pelo que sei, o SQL dinâmico era um recurso relativamente tardio; o uso inicial do SQL era principalmente pré-compilado / pré-processado com parâmetros para valores (entrada e saída), portanto, os parâmetros nas consultas podem anteceder a injeção de SQL no software (talvez não nas consultas ad-hoc / CLI).
Mark Rotteveel

6
Eles podem anteceder o reconhecimento da injeção de SQL.
user253751

20

Simplesmente disse: Eles não o fizeram. Sua declaração:

Por que o mecanismo de prevenção de injeção SQL evoluiu na direção do uso de consultas parametrizadas?

é fundamentalmente falho. As consultas parametrizadas existem há muito mais tempo do que a injeção de SQL é pelo menos amplamente conhecida. Eles geralmente foram desenvolvidos como uma maneira de evitar a concentração de strings na funcionalidade usual "formulário para pesquisa" que os aplicativos LOB (Line of Business) possuem. Muitos - MUITOS - anos depois, alguém encontrou um problema de segurança com a manipulação de strings.

Lembro-me de fazer SQL há 25 anos (quando a Internet NÃO era amplamente usada - estava apenas começando) e lembro de fazer SQL vs. IBM DB5 IIRC versão 5 - e que já tinham consultas parametrizadas.


obrigado. Por que havia uma necessidade de evitar a concatenação de cadeias? Parece-me que isso seria uma característica útil. Alguém teve um problema com isso?
Dennis

3
Dois na verdade. Primeiro, nem sempre é totalmente trivial - por que lidar com alocação de memória etc. quando não é necessário. Mas segundo, nos tempos antigos, o cache de desempenho do lado do banco de dados sql não era exatamente tão bom - a compilação do SQL era cara. Como efeito colateral do uso de uma instrução SQL preparada (que é de onde vêm os parâmetros), os planos de execução podem ser reutilizados. O SQL Server introduziu a parametrização automática (para reutilizar os planos de consulta mesmo sem parâmetros - eles são deduzidos e implícitos). Acho que 2000 ou 2007 - em algum lugar, IIRC.
TomTom

2
Ter consultas parametrizadas não elimina a capacidade de concatenação de cadeias. Você pode fazer a concatenação de cadeias para gerar uma consulta parametrizada. Só porque um recurso é útil, não significa que é sempre uma boa escolha para um determinado problema.
JimmyJames 14/09/16

Sim, mas como eu disse - no momento em que foram inventados, o SQL dinâmico vinha com um desempenho bastante decente;) Ainda hoje as pessoas dizem que os planos de consultas SQL dinâmicas no sql server não são reutilizados (o que está errado desde que - hm - como Eu disse algum ponto entre 2000 e 2007 - muito tempo). Naquele tempo velho você realmente queria declarações preparadas, se você executar o SQL várias vezes;)
TomTom

Caching plano para SQL dinâmica era de fato adicionado ao SQL Server 7.0, em 1998 - sqlmag.com/database-performance-tuning/...
Mike Dimmick

13

Além de todas as outras boas respostas:

A razão pela qual o nº 2 é melhor é porque ele separa seus dados do seu código. No nº 1, seus dados fazem parte do seu código e é daí que vêm todas as coisas ruins. Com o nº 1, você obtém sua consulta e precisa executar etapas adicionais para garantir que sua consulta entenda seus dados como dados, enquanto que no nº 2 você obtém seu código e seu código e seus dados são dados.


3
Separar código e dados também significa que suas defesas contra a injeção de código hostil são gravadas e testadas pelo fornecedor do banco de dados. Portanto, se algo passado como parâmetro junto com uma consulta inofensiva acabar destruindo ou subvertendo seu banco de dados, a reputação da empresa estará em risco e sua organização poderá processá-los e vencer. Isso também significa que, se esse código contiver um bug explorável, as chances são boas de que seja o site de outra pessoa em que todos os pedaços se soltem, e não o seu. (Só não ignorar as correções de segurança!)
nigel222

11

As consultas parametrizadas, além de fornecerem defesa contra injeção de SQL, geralmente têm um benefício adicional de serem compiladas apenas uma vez e executadas várias vezes com parâmetros diferentes.

Do ponto de banco de dados SQL de vista select * from employees where last_name = 'Smith'e select * from employees where last_name = 'Fisher'são muito diferentes e, portanto, requerem separado de análise, compilação e otimização. Eles também ocuparão slots separados na área de memória dedicada ao armazenamento de instruções compiladas. Em um sistema muito carregado com um grande número de consultas semelhantes que têm parâmetros diferentes, a computação e a sobrecarga de memória podem ser substanciais.

Posteriormente, o uso de consultas parametrizadas geralmente oferece grandes vantagens de desempenho.


Eu acho que essa é a teoria (baseada em instruções preparadas usadas para consultas parametrizadas). Na prática, duvido que esse seja realmente o caso, pois a maioria das implementações apenas prepara-vincula-executa em uma chamada, portanto, use uma instrução preparada diferente para cada consulta parametrizada, a menos que você tome medidas explícitas para realmente preparar instruções (e uma biblioteca -level preparegeralmente é bem diferente de um nível SQL real prepare).
jcaron

As seguintes consultas também são diferentes para o analisador SQL: SELECT * FROM employees WHERE last_name IN (?, ?)e SELECT * FROM employees WHERE last_name IN (?, ?, ?, ?, ?, ?).
Damian Yerrick 14/09/16

Sim eles tem. É por isso que a MS adicionou o cache do plano de consulta em 1998 ao SQL Server 7. Como em: Suas informações são antigas.
TomTom

11
@TomTom - o cache do plano de consulta não é o mesmo que a parametrização automática, na qual você parece estar sugerindo. Leia antes de publicar.
mustaccio 14/09/16

@mustaccio Na verdade, pelo menos, a Microsoft apresentou os dois ao mesmo tempo.
TomTom

5

Espere mas por quê?

A opção 1 significa que você deve escrever rotinas de limpeza para qualquer tipo de entrada, enquanto a opção 2 é menos suscetível a erros e menos código para você escrever / testar / manter.

Certamente, "cuidar de todas as advertências" pode ser mais complexo do que você pensa, e sua linguagem (por exemplo, Java PreparedStatement) tem mais informações do que você pensa.

Instruções preparadas ou consultas parametrizadas são pré-compiladas no servidor de banco de dados, portanto, quando os parâmetros são definidos, nenhuma concatenação SQL é feita porque a consulta não é mais uma sequência SQL. Uma vantagem adicional é que o RDBMS armazena em cache a consulta e as chamadas subseqüentes são consideradas o mesmo SQL, mesmo quando os valores dos parâmetros variam, enquanto no SQL concatenado toda vez que a consulta é executada com valores diferentes, a consulta é diferente e o RDBMS precisa analisá-la. , crie o plano de execução novamente etc.


11
O JDBC não higieniza anithing. O protocolo tem uma parte específica para o parâmetro e o DB simplesmente não interpreta esses parâmetros. É por isso que você pode definir o nome da tabela a partir do parâmetro
talex 13/09/16

11
Por quê? se o parâmetro não for analisado ou interpretado, não há razão para escapar de algo.
TAlex

11
Eu acho que você tem a imagem errada de como funciona uma consulta parametrizada. Não é apenas o caso dos parâmetros serem substituídos posteriormente, eles nunca são substituídos . Um DBMS transforma qualquer consulta em um "plano", um conjunto de etapas que serão executadas para obter seu resultado; em uma consulta parametrizada, esse plano é como uma função: ele possui várias variáveis ​​que precisam ser fornecidas quando executadas. No momento em que as variáveis ​​são fornecidas, a string SQL foi completamente esquecida e o plano é executado apenas com os valores fornecidos.
IMSOP

2
@IMSoP Isso foi um equívoco meu. Embora eu ache que é comum, como você pode ver nas duas respostas mais votadas para esta pergunta em SO stackoverflow.com/questions/3271249/… . Eu li sobre isso e você está certo. Eu editei a resposta.
Tulains Córdova 13/09/16

3
@ TomTom Isso é ótimo para desempenho , mas não faz nada para segurança . Quando uma parte comprometida da SQL dinâmica é compilada e armazenada em cache, o programa já foi alterado . A criação de um plano a partir de SQL parametrizado não dinâmico e a transmissão de elementos de dados ainda é fundamentalmente diferente de um DBMS que abstrai a semelhança entre duas consultas apresentadas como seqüências SQL completas.
IMSoP

1

Vamos imaginar como seria uma abordagem ideal de "higienizar, filtrar e codificar".

A limpeza e a filtragem podem fazer sentido no contexto de um aplicativo específico, mas, no final das contas, ambas se resumem a dizer "você não pode colocar esses dados no banco de dados". Para o seu aplicativo, isso pode ser uma boa ideia, mas não é algo que você pode recomendar como solução geral, pois haverá aplicativos que precisam ser capazes de armazenar caracteres arbitrários no banco de dados.

Então isso deixa a codificação. Você pode começar por ter uma função que codifica as strings adicionando caracteres de escape, para que você possa substituí-los em si mesmo. Como bancos de dados diferentes precisam de caracteres diferentes que escapam (em alguns bancos de dados, ambos \'e ''são seqüências de escape válidas para ', mas não em outros), essa função precisa ser fornecida pelo fornecedor do banco de dados.

Mas nem todas as variáveis ​​são seqüências de caracteres. Às vezes, você precisa substituir um número inteiro ou uma data. Eles são representados de maneira diferente às seqüências de caracteres, portanto, você precisa de métodos de codificação diferentes (novamente, eles precisam ser específicos para o fornecedor do banco de dados) e precisa substituí-los na consulta de maneiras diferentes.

Portanto, talvez as coisas fiquem mais fáceis se o banco de dados também substituir por você - ele já sabe quais tipos a consulta espera, e como codificar dados com segurança, e como substituí-los na sua consulta com segurança, para que você não precise se preocupar com isso. no seu código.

Neste ponto, apenas reinventamos as consultas parametrizadas.

E, quando as consultas são parametrizadas, elas abrem novas oportunidades, como otimizações de desempenho e monitoramento simplificado.

É difícil fazer codificação correta, e a codificação feita corretamente é indistinguível da parametrização.

Se você realmente gosta de interpolação de string como uma forma de consultas de construção, há um par de idiomas (Scala e ES2015 vêm à mente) que têm interpolação de string pluggable, por isso não são bibliotecas que permitem que você escrever consultas parametrizadas que se parecem com interpolação de string, mas estão seguros contra injeção de SQL - portanto, na sintaxe do ES2015:

import {sql} from 'cool-sql-library'

let result = sql`select *
    from users
    where user_id = ${user_id}
      and password_hash = ${password_hash}`.execute()

console.log(result)

11
"É difícil fazer codificação certa" - hahaha. Não é. Um dia ou dois, está tudo documentado. Eu escrevi um codificador há muitos anos para um ORM (porque o servidor sql tem um limite de parâmetros e, portanto, é problemático inserir 5000-10000 linhas em uma instrução (há 15 anos atrás) .Não me lembro de ser um grande problema.
TomTom

11
Talvez o SQL Server seja suficientemente regular para que não seja um problema, mas encontrei problemas em outros bancos de dados - casos de canto com codificações de caracteres incompatíveis, opções de configuração obscuras, problemas de data e número específicos de localidade. Tudo solucionável, mas precisando de pelo menos um entendimento superficial das peculiaridades do banco de dados (estou olhando para você, MySQL e Oracle).
James_pic

3
A codificação @TomTom é realmente muito difícil de acertar quando você leva em consideração o tempo. O que você faz quando o fornecedor do banco de dados decide criar um novo estilo de comentário na próxima versão ou quando uma palavra de barra se torna uma nova palavra-chave em uma atualização? Teoricamente, você poderia obter a codificação realmente certa para uma versão do seu RDBMS e estar errada na próxima revisão. Nem sequer começar a fazer o que acontece quando você trocar de fornecedor para um que tem comentários condicionais usando a sintaxe fora do padrão
Eric

@ Eric, isso é francamente horrível. (Eu uso Postgres e, se ele tem quaisquer verrugas bizarros tenho ainda de encontrá-los.)
Wildcard

0

Na opção 1, você está trabalhando com um conjunto de entradas size = infinito que está tentando mapear para um tamanho de saída muito grande. Na opção 2, você limitou sua entrada ao que escolher. Em outras palavras:

  1. Triagem e filtragem cuidadosas [ infinito ] para [ todas as consultas SQL seguras ]
  2. Usando [ cenários pré-considerados limitados ao seu escopo ]

De acordo com outras respostas, também parece haver alguns benefícios de desempenho ao limitar seu escopo para longe do infinito e para algo gerenciável.


0

Um modelo mental útil do SQL (especialmente dialetos modernos) é que cada instrução ou consulta SQL é um programa. Em um programa executável binário nativo, os tipos mais perigosos de vulnerabilidades de segurança são excedentes, nos quais um invasor pode substituir ou modificar o código do programa com instruções diferentes.

Uma vulnerabilidade de injeção SQL é isomórfica a um estouro de buffer em uma linguagem como C. A história mostrou que os estouros de buffer são extremamente difíceis de evitar - mesmo o código extremamente crítico sujeito à revisão aberta geralmente contém essas vulnerabilidades.

Um aspecto importante da abordagem moderna para solucionar vulnerabilidades de estouro é o uso de mecanismos de hardware e SO para marcar partes específicas da memória como não executáveis ​​e marcar outras partes da memória como somente leitura. (Consulte o artigo da Wikipedia sobre Proteção de espaço executável , por exemplo.) Dessa forma, mesmo que um invasor possa modificar dados, o invasor não pode fazer com que seus dados injetados sejam tratados como código.

Portanto, se uma vulnerabilidade de injeção de SQL é equivalente a um estouro de buffer, qual é o equivalente de SQL a um bit NX ou a páginas de memória somente leitura? A resposta é: instruções preparadas , que incluem consultas parametrizadas mais mecanismos semelhantes para solicitações que não são de consulta. A instrução preparada é compilada com certas partes marcadas como somente leitura, para que um invasor não possa alterar essas partes do programa e outras partes marcadas como dados não executáveis ​​(os parâmetros da instrução preparada), nos quais o invasor pode injetar dados, mas que nunca será tratado como código de programa, eliminando assim a maior parte do potencial de abuso.

Certamente, higienizar a entrada do usuário é bom, mas para estar realmente seguro, você precisa ser paranóico (ou, equivalente, pensar como um invasor). Uma superfície de controle fora do texto do programa é a maneira de fazer isso, e instruções preparadas fornecem essa superfície de controle para SQL. Portanto, não surpreende que declarações preparadas e, portanto, consultas parametrizadas, sejam a abordagem recomendada pela grande maioria dos profissionais de segurança.


Tudo isso é legal e elegante, mas não aborda a questão de acordo com o título.
TomTom

11
@TomTom: Como assim? A questão é exatamente por que as consultas parametrizadas são o mecanismo preferido para impedir a injeção de SQL; minha resposta explica por que as consultas parametrizadas são mais seguras e robustas do que a higienização da entrada do usuário.
Daniel Pryden 14/09/16

Sinto muito, mas MINHA pergunta diz: "Por que o mecanismo de prevenção de injeção SQL evoluiu na direção do uso de consultas parametrizadas?". Eles não. Não é sobre o agora, é sobre a história.
TomTom

0

Eu já escrevi sobre isso aqui: https://stackoverflow.com/questions/6786034/can-parameterized-statement-stop-all-sql-injection/33033576#33033576

Mas, apenas para simplificar:

A maneira como as consultas parametrizadas funcionam é que o sqlQuery é enviado como uma consulta e o banco de dados sabe exatamente o que essa consulta fará, e só então inserirá o nome de usuário e as senhas apenas como valores. Isso significa que eles não podem efetuar a consulta, porque o banco de dados já sabe o que a consulta fará. Portanto, nesse caso, ele procuraria um nome de usuário "Ninguém OU 1 = 1 '-" e uma senha em branco, que deve aparecer como falsa.

Porém, essa não é uma solução completa e a validação de entrada ainda precisará ser feita, pois isso não afetará outros problemas, como ataques XSS, pois você ainda pode colocar o javascript no banco de dados. Se isso for lido em uma página, ele será exibido como javascript normal, dependendo de qualquer validação de saída. Então, a melhor coisa a fazer ainda é usar a validação de entrada, mas usar consultas parametrizadas ou procedimentos armazenados para interromper qualquer ataque SQL


0

Eu nunca usei SQL. Mas obviamente você ouve quais problemas as pessoas têm e os desenvolvedores de SQL tiveram problemas com essa coisa de "injeção de SQL". Durante muito tempo, não consegui descobrir. E então percebi que as pessoas estavam criando instruções SQL, instruções de origem SQL textuais reais, concatenando seqüências de caracteres, algumas das quais inseridas por um usuário. E meu primeiro pensamento nessa realização foi choque. Choque total. Pensei: como alguém pode ser tão ridiculamente estúpido e criar declarações em qualquer linguagem de programação como essa? Para um desenvolvedor de C, C ++, Java ou Swift, isso é loucura total.

Dito isso, não é muito difícil escrever uma função C que use uma string C como argumento e produza uma string diferente que se pareça exatamente com uma literal de string no código-fonte C que representa a mesma string. Por exemplo, essa função converteria abc em "abc" e "abc" em "\" abc \ "" e "\" abc \ "" em "\" \\ "abc \\" \ "". (Bem, se isso parece errado para você, é html. Estava certo quando eu o digitei, mas não quando é exibido.) E uma vez que a função C é escrita, não é difícil gerar código fonte C onde o texto de um campo de entrada fornecido pelo usuário é transformado em um literal de string C. Isso não é difícil de proteger. Por que os desenvolvedores de SQL não usariam essa abordagem como uma maneira de evitar injeções de SQL está além de mim.

"Higienizar" é uma abordagem totalmente falha. A falha fatal é que ela torna certas entradas do usuário ilegais. Você acaba com um banco de dados em que um campo de texto genérico não pode conter texto como; Solte a tabela ou o que você usaria em uma injeção SQL para causar danos. Acho isso inaceitável. Se um banco de dados armazena texto, ele deve poder armazenar qualquer texto. E a falha prática é que o desinfetante parece não acertar :-(

Obviamente, consultas parametrizadas são o que qualquer programador usando uma linguagem compilada estaria esperando. Torna a vida muito mais fácil: você tem alguma entrada de string e nem se importa em convertê-la em uma string SQL, mas apenas a transmite como parâmetro, sem chance de nenhum caractere dessa string causar dano.

Portanto, do ponto de um desenvolvedor que usa linguagens compiladas, higienizar é algo que nunca me ocorreria. A necessidade de higienização é insana. Consultas parametrizadas são a solução óbvia para o problema.

(Achei a resposta de Josip interessante. Ele basicamente diz que, com consultas parametrizadas, você pode interromper qualquer ataque contra o SQL, mas pode ter um texto em seu banco de dados usado para criar uma injeção de JavaScript :-( Bem, temos o mesmo problema novamente , e não sei se o Javascript tem uma solução para isso.


-2

O principal problema é que os hackers encontraram maneiras de cercar o saneamento, enquanto as consultas parametrizadas eram um procedimento existente que funcionava perfeitamente com os benefícios extras de desempenho e memória.

Algumas pessoas simplificam o problema como "são apenas aspas simples e duplas", mas os hackers encontraram maneiras inteligentes de evitar a detecção, como usar codificações diferentes ou usar funções de banco de dados.

De qualquer forma, você só precisava esquecer uma única sequência para criar uma violação de dados catastrófica. Os hackers foram capazes de automatizar scripts para baixar o banco de dados completo com uma série ou consultas. Se o software for conhecido como um pacote de código aberto ou um famoso conjunto de negócios, você poderá simplesmente anexar a tabela de usuários e senhas.

Por outro lado, o uso de consultas concatenadas era apenas uma questão de aprender a usar e se acostumar.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.