OWASP Top 10: de Injections a Broken Access Control | Alura Cursos Online
Primeiras aulas do curso OWASP Top 10: de Injections a Broken Access Control
OWASP Top Ten e Injection - Introdução
Bem-vindo a esse curso de OWASP Top 10, OWASP é uma organização, Top 10 são os riscos mais críticos para aplicações web, o top 10. Claro, esse relatório sai de anos em anos. O último relatório, o mais atualizado de 2017, contém mudanças desde o anterior, de 4 a 5 anos antes.
Então, quer dizer, de tempo em tempo nós vamos ver atualizações. Isso não significa que quando sai um relatório novo, o antigo é jogado fora. Na verdade, quase tudo do antigo se mantém e basicamente adicionam coisas novas ou alteram algumas recomendações.
Nesse primeiro curso nós vamos ver os cinco primeiros desse Top 10, quais são os principais ricos que uma aplicação web, e claro, toda aplicação que utiliza web como infraestrutura também passa por esse problema. Então se nós estamos falando de um aplicativo que utiliza web para se comunicar com servidor, lá nós vamos ter esses riscos.
Então nós vamos falar sobre esses riscos, onde eles aparecem, como nós podemos tentar retificá-los, se nós estivermos passando por isso; como nós podemos atacar eles de uma maneira estática, analisando código-fonte, por exemplo; ou de maneira dinâmica, testando. Como nós podemos resolver esses problemas e tentar diminuir esses riscos ou as chances de eles ocorrerem conosco.
Claro, para você que é desenvolvedor ou desenvolvedora de uma aplicação web ou de serviços web, é muito bom nós aprendermos isso para que nós criemos um hábito de boas práticas de segurança.
E para você que é da equipe de segurança ou de uma equipe de desenvolvimento que não tem equipe de segurança, mas ficou responsável pela segurança; nesse caso nós também aprendemos bastante para que nós possamos criar as nossas camadas de segurança e para que nós ajudemos todas as equipes dentro da nossa empresa e dos nossos produtos.
Então a ideia aqui é que nós utilizarmos técnicas defensivas, para que nós possamos nos prevenir de possíveis riscos e ataques que possam acontecer no mundo afora. Vamos lá? Vamos começar!
OWASP Top Ten e Injection - A1 Injection
Então, vamos começar! Se eu vou começar, primeiro eu vou entrar na organização OWASP que produz aqui a lista de riscos top 10 dela, além de diversos outros relatórios que nós vamos analisar em outros cursos. Mas tem um top 10 de segurança, de diversos tipos de aplicações.
No nosso caso, nós vamos estar falando aplicações web, então dentro do OWASP você vai encontrar aqui top 10. O top 10 são os dez principais riscos de segurança, nesse caso aqui de aplicações web.
Você pode ir navegando por cada um desses, é uma das abordagens, você pode ir navegando cada um desses. A versão atual aqui, a mais recente, é a versão de 2017. Essa é a versão mais recente.
Você pode baixar também o PDF. Eu estou baixando aqui o PDF. Baixei o PDF, tem o PDF em português também, eu vou usar aqui com os termos em inglês. Eu vou assumir aqui os termos em inglês para que nós nos acostumemos com as duas coisas. Claro, vamos sempre estar conversando em português aqui, nesse instante.
Então eu tenho aqui o meu PDF, nesse PDF nós vamos ter os 10. Só que você fala: "Guilherme, isso é de 2017! Eles não lançam todo ano?" Não, não lançam todo ano! De tantos em tantos anos eles lançam, fazem a pesquisa etc. e aí tem mudanças do último relatório.
Se o último relatório foi 2013, 2012, para 2017, tem mudanças e eles falam: "olhe, de 2013 para 2017, o que mudou, por exemplo?" Então aqui tiveram dois riscos que foram juntados em um único risco. Alguns riscos ficaram, eles consideraram os que são mais importantes, tem mais risco do que outros. Alguns saíram e outros entraram.
Então tem cada um em uma situação, acontece algumas mudanças, então é sempre importante. Não importa o ano que nós estejamos, nós sempre olhamos se tem alguma alteração e se tem alguma coisa nova. Ou às vezes até olhamos em um relatório antigo, se tem algum que pode nos afetar bastante.
De qualquer maneira, nós vamos atacar aqui o top 10, que é esse daqui. O primeiro deles é o "A1: 2017 – Injection". Então, o que é injeção? Vai ter um slide aqui nesse PDF específico descrevendo cenários de injeção, como funciona etc.
Aqui no site também, você pode clicar em "Injection", que você vai ter uma descrição também, diversas descrições, exemplos etc. do que nós podemos fazer. Então, o que eu queria citar? Eu queria que nós entrássemos em cada um desses tópicos, injeção, etc. etc. A2, A3, A4, até o A10 e que nós fossemos discutindo como funciona o ataque, como nós temos que dar uma olhada no código para encontrarmos que tipo de código que pode gerar esse problema.
Então tudo isso, nós vamos dando uma olhada à medida que nós analisamos essa ficha, por exemplo, a ficha de "Injection" que tem essas informações apresentadas dessa maneira, ou aqui no próprio site, que tem as informações apresentadas de outra maneira. Claro, ambas possuem o mesmo tipo de problema, estão discutindo o mesmo problema para nós.
Então, vamos lá! Primeiro com injeção, nós temos que lembrar que quando nós estamos na internet, nós estamos com um servidor longe e nós somos um cliente aqui nesse canto. Então toda vez que eu estou acessando um site - por exemplo: próprio site da OWASP - eu estou no meu cliente, que é o meu navegador, mandando uma requisição para o servidor e o servidor devolvendo uma resposta.
Então nós sempre estamos fazendo isso. Então quando eu estou em um site da Alura, por exemplo, e eu clico aqui em "ENTRAR", eu estou falando: "olhe, me dê o link, esse daqui. Me dê a página dessa URL para o servidor da Alura" e a Alura me devolve essa página.
E agora, quando eu preencho aqui os meus dados, preencho uma senha minha aqui e clico em "ENTRAR", eu também estou enviando dados e recebendo repostas. A questão é: isso daqui é a base dos cursos de web que nós ensinamos aqui na Alura. Agora, a base é: o que pode ser feito em situações como essa?
Repare que um formulário é uma situação diferente. À primeira vista de um link qualquer... Vou clicar nesse link pressionando a tecla "Ctrl" para abrir em outra aba. Ele simplesmente pede esses dados, pede e me dá a página que está nessa URI, me dá o documento que essa nessa URI. Ele devolve e acabou.
Aqui nós não estamos pedindo só o documento, nós estamos enviando informações extras e provavelmente no outro lado do servidor ele vai fazer alguma coisa com essas informações. O que ele vai fazer com essas informações? Com o e-mail e com a senha? Vai procurar no banco de dados se existe um usuário com esse e-mail e com essa senha.
Então quando eu clico em "ENTRAR", o servidor da Alura ou o servidor de qualquer outro site, vai procurar no banco de dados e nos devolver. "Encontrei" ou "não encontrei" - no nosso caso, não encontrou porque não é essa a minha senha.
Na verdade, isso também acontece: quando nós estamos acessando cursos online em mobile, isso daqui pode ser uma página fixa, que alguém digitou manualmente, mas provavelmente - e eu claramente sei disso - isso daqui, na verdade, é um padrão que busca no banco de dados todos os cursos de mobile para nós.
Então, mesmo aqui, nós temos uma busca no banco de dados. Os bancos de dados hoje em dia estão em todo lugar que nós queremos ter um comportamento dinâmico, é muito comum nós termos um banco de dados.
E o banco de dados não é necessariamente SQL, então repare que, o que eu estou dizendo é que em requisições de web é muito comum que uma aplicação do outro lado vá buscar coisas no banco, ou vá fazer alguma coisa, fazer alguma coisa com informações que nós, o usuário final, fornece. Seja isso daqui ou usuário e senha, por exemplo. Tudo são informações que o usuário passa para o servidor.
E o servidor deve confiar que essa informação foi passada de forma adequada? Não dá para saber! Você não tem como saber se você seguiu o link, se a pessoa que digitou isso daqui realmente clicou em um link que existia, ou se ela inventou um link que não existe. O servidor não sabe. Por padrão, não tem como confiar que toda requisição de um cliente veio com parâmetros, seja a própria URI um parâmetro que faz sentido e que está certinho etc.
Então aqui também, eu não tenho como confiar que a pessoa digitou realmente um e-mail ou Gmail, sei lá, @caelum. Talvez ela tenha digitado isso daqui. Isso daqui nem é um e-mail. Tentei enviar. Beleza, a página aqui percebeu que não é um e-mail e reclamou!
Agora, se eu colocar um "@" isso daqui vira um e-mail? Ainda não vira! Se eu colocar isso daqui vida um e-mail? Depende, até pode ser um e-mail local, é que aqui tem um ponto e vírgula que não deveria ter.
E esse daqui? Essa daqui é um e-mail? Esse é um e-mail válido! Em geral, as pessoas não utilizam e-mails desse gênero, mas poderia ser validado, um usuário e senha inválidos.
Então repare que o usuário final pode enviar o que quiser. Na verdade, você fala: "não, não pode! Porque ele não conseguiu enviar e-mail!" Mais ou menos! Lembra que isso daqui é uma requisição que o navegador está enviando para o servidor? Se é o navegador que está enviando para o servidor e eu quiser tentar esculhambar, fuçar, cutucar ou penetrar no seu site; o que eu posso fazer?
Eu posso tentar enviar coisas diferentes e eu posso tentar enviar algum comando diferente, que eu descubro alguma coisa do seu servidor. Então seja aqui no parâmetro da URI, seja aqui em um parâmetro de - aqui estou chamando de "parâmetro", formalmente não é chamado "parâmetro", aqui é "URI" mesmo.
Mas eu sei que isso daqui é utilizado para ler o banco de dados em todos os sites. Quando você entra em um site de jornal, aquela URI, em geral, é utilizada para trazer uma informação do banco, como usuário e senha, para trazer informação de algum banco ou de algum lugar.
Todos esses lugares recebem informações do usuário final e nós temos que tomar alguns cuidados. Por quê? Porque é onde o usuário final pode injetar coisas em nós, ou injetar seja onde for. Seja para buscar coisa em banco, seja para fazer outras coisas.
Então, qual é um exemplo? Primeira coisa seria aqui nesse e-mail. Então vamos pensar como é esse exemplo de login. Eu vou pegar aqui um TextEdit simples e vou escrever query simples de SQL: "select * from Usuarios where email='gruilherme@xpto.com.br' and senha=h89t243hnuewfthu". Essa senha aqui, por exemplo. Vamos assumir que é isso, coloquei uma tecla ";" ali.
Vamos assumir que a query é essa. Se a query é essa para buscar um usuário que tenha esse usuário e essa senha no banco, parece ser OK, parece que ela busca. Existem algumas coisas que nós acabamos fazendo a mais.
Nós costumamos limitar a um só retorno, para termos certeza que nós só traremos um usuário, só o primeiro. Nós não precisamos de mais de um se tivermos o mesmo e-mail e senha. Até porque, nós não esperamos encontrar isso. Eu poderia trazer só o ID do usuário se eu quiser, então eu estou colocando restrições.
Mas de qualquer maneira, isso daqui serviu para validarmos e verificarmos se o usuário está lá ou não. Isso daqui seria se o usuário fosse sempre "Guilherme" e sempre essa senha. Mas não é!
O nome do usuário vem de alguma maneira na nossa requisição web, do "request". Então, por exemplo: em Java seria comum termos um "request.geParameter("email");" então nós lemos o e-mail do parâmetro.
A senha seria um "request.getParameter("senha");". Quer dizer, estou dizendo que é muito comum, dependo do framework que você usa etc. e a SQL aqui, estou colocando "String".
Não precisaria ser "String", pode ser um "var", pode ser várias outras coisas aqui. Seria o quê? Seria isso tudo. Vou colocar tudo isso entre aspas, vou tirar o "limit 1", só para nós testarmos enquanto isso. Testarmos mesmo, não vamos, mas é só para nós vermos.
Então temos esse "select" aqui, bonitão. Você fala: "aqui, Guilherme! Mas o e-mail aqui você tem que colocar aquele e-mail!" Maravilha! Então fechei e abri aspas. O e-mail e agora aqui a senha. Fechei e abri aspas. Senha.
Então, o que eu estou fazendo aqui? Eu estou criando uma nova "string" que coloca o valor do e-mail e o valor da senha aqui dentro. Maravilha! Então, por exemplo: se o usuário for "Guilherme" e a senha for essa daqui, qual é o resultado que eu vou ter? O resultado que eu vou ter, vou digitar: "select * from Usuarios where email='guilherme' and senha='87t9h3h'". Esse código que eu coloquei, maravilhoso. Fecho isso daqui. Então, essa seria a "string".
Então parece funcionar. Funciona e não tem problema. Funciona! Quer dizer, duas frases. A primeira eu errei, tem problema. Qual é o problema aqui? O problema é que eu não estou validando o usuário e a senha antes de utilizar ele no meu banco. Vamos dar um exemplo? Olhe um exemplo: ao invés do Guilherme e isso daqui, eu vou colocar outro usuário e outra senha.
Então aqui no campo de e-mail, em vez de eu digitar o meu e-mail de verdade, eu digito ou eu coloco em uma requisição web. Seja lá como for, eu coloco isso daqui: "' or "admin"=true". Tem ainda alguns cuidados aqui para nós tomarmos, por enquanto eu vou deixar assim. Então seria isso e a senha, eu coloco uma senha qualquer.
Então vamos olhar. Se eu fizesse isso, o que ia acontecer? Então, "' or "admin"=true", então qual seria o resultado da minha query SQL? "select * from Usuarios where e-mail='" aquele código que eu coloquei de e-mail, que é isso daqui. O que está aqui para direita, que é o "' and senha=", uma senha qualquer que eu digitei, sem ponto e vírgula.
Então ficou esse código, já está aparecendo. Está percebendo que dá para acontecer algo de errado aqui? Porque a query, olhe, por enquanto até aqui, ela é válida. Eu estou buscando alguém que tenha esse e-mail ou seja "admin".
Mas aqui ficou confuso, aqui é algo que não faz sentido. Esse SQL aqui não é válido, então o que eu faço? Coloco um comentário em SQL. Em SQL nós podemos colocar um comentário, SQL comments.
Então comentários de SQL, aqui eu estou colocando do SQL do Oracle, mas você pode pegar aqui, tem várias maneiras de você pegar. Então vou pegar esse daqui do MySQL, então ele pode ser um cerquilha. Então aqui ele começou, ele foi para frente, "- -" "/* */". São várias maneiras.
Então, o que nós podemos fazer se nós descobrimos que do outro lado é MySQL? Nem precisamos descobrir, é só testarmos. Colocamos aqui um cerquilha. Se eu colocar um cerquilha aqui, como é que vira a query? A query vira isso daqui, isto é, tudo isso vai ser interpretado pelo banco. Tudo isso vai ser ignorado pelo banco.
O que a query vai trazer? Vai trazer alguém que tenha um e-mail vazio. Ninguém deve ter um e-mail vazio ou seja "admin" e ele vai me trazer o usuário que é "admin" e eu vou me logar com qual usuário? Em vez de eu me logar com o usuário "Guilherme", eu vou me logar com um usuário que eu não sei quem é, mas que é "admin".
Claro, eu estou chutando que o banco de dados na tabela usuários tenha uma coluna chamada "admin" e que ela seja booleana, 01 ou algo do gênero, que seja equivalente no banco que nós estamos utilizando etc.
Mas não importa, se eu não validei esse código aqui, esse texto inserido pelo meu usuário, ele pode injetar coisas aqui que executam coisas que eu não gostaria que fossem executadas. Então aqui é um exemplo de injeção (injection).
É um exemplo de injeção de código SQL. Então com esse exemplo nós temos uma quebra de autorização e autenticação. A pessoa pode se logar com outro usuário e acessar coisa que nós não gostaríamos que acessasse.
Então essa abordagem de concatenar "string" é muito ruim - pior ainda, a pessoa poderia escrever, simplesmente, " "; " e um outro comando. Depois do ponto e vírgula você pode colocar outro comando, em vez de "create" poderia ser um "drop table Usuarios". Imagine? E o ";#" aqui para terminar. Então esse daqui seria o meu código.
Então, o que ele iria fazer? Ele iria executar o "select", iria dar um "drop table" e depois iria jogar fora o resto. Imagine o perigo! Então, não só a pessoa conseguiria se logar, como ela conseguira fazer alterações no nosso banco, e muito mais, eu abri a porta através da injeção de SQL.
Então, de alguma maneira, eu preciso validar que os dados informados pelos meus usuários não vão alterar o meu banco de uma maneira indevida ou não vão acessar coisas indevidas. Então, quer dizer, concatenação de "string" perigosa.
Você fala: "Guilherme, então você sabe o que você faz?" Se tiver umas aspas simples, e "scape", faça um " "scape"", coloque uma barra invertida, faça um "replaceAll" na "string" em Java ou em qualquer linguagem. Então você faz um " "scape"", então isso daqui iria virar o seguinte: ele ia virar uma " '/' " e isso daqui ia ser ignorado, mas o comentário ainda não.
Você fala: "beleza, Guilherme! Então escapa também o comentário!" Estou chutando que o " "scape"" de comentário é assim, não importa. Ele escapa o comentário. Funcionaria, voltaria a funcionar, só que repare que nós estamos correndo alguns riscos agora.
Por quê? Será que eu me protegi de todos os casos? Com certeza, não! Com certeza nós já vimos aqui que tem outros tipos de comentários, nós já vimos que tem outros tipos de comentários. E a "string" com aspas duplas? SQL aceita "string" com aspas duplas? SQL aceita o quê?
Então você teria que proteger de tanta coisa que você limpar essa "string", sanitizar - e esse é o processo de "string sanitization", "SQL sanitization", tem de tudo de sanitização, validação e sanitização.
Então sanitizar é modificar esse "input". Aqui eu estou caindo em um site da Oracle. É modificar o "input" para garantir que ele é válido. Isso daqui é validar e garantir que ele siga certas regras.
Então é importante nós validarmos no cliente? É! É suficiente validar no cliente? Nunca! Ou 99.99% das vezes, vai que... É importante validar no servidor? Sim! É importante sanitizar? Sim, mas é trabalhoso! É trabalhoso ao ponto de que sempre pode ter alguma coisa que nós deixamos aberto e fica um jogo de gato e rato, ou algo do gênero. Não sei como é o provérbio, ou algo do gênero.
Mas ficam um atrás do outro. Você protege um, e alguém encontra uma outra forma de hackear o outro, que encontra uma outra forma, e nós ficamos loucos com isso. Então ao invés de nós fazermos isso manualmente, nós usamos bibliotecas que vão evitar esse tipo de concatenação. As próprias bibliotecas farão a sanitização para nós.
Seja uma biblioteca, por exemplo, se você usa Java - o Hibernate, por exemplo - pode ter lá uma maneira de usar o Hibernate que faz isso para nós. O próprio JDBC do Java faz isso. Todas as ferramentas de linguagem de programação costumam ter uma maneira que não envolve concatenação.
Então concatenação, jamais, em qualquer linguagem, para executar qualquer comando, concatenam informação que o usuário informou "injection" ou abriu a porta para "injection". Então use uma biblioteca que já nos faz o "scape", a sanitização.
E mesmo assim, às vezes não é a biblioteca, às vezes é o driver que conecta ao MySQL. O driver que conecta o lugar, que vai fazer esse "scape", Melhor ainda, você tem mais uma garantia!
Então, quer dizer, vamos dar uma olhada no nosso documento. Ele fala assim: "olhe, sua aplicação está vulnerável?" Quando que sua aplicação está vulnerável? Se os dados fornecidos pelo seu usuário não são validados, você fala: "mas foi validado no cliente!" Não dá para confiar no cliente!
O cliente desativa essa validação, o cliente usa um "command-line", o cliente pode usar o que quiser para fazer. Ele pode desativar a validação, não foi filtrado, tem que filtrar de alguma maneira, sanitizando pela aplicação. Então no minuto que nós sanitizamos, usamos uma ferramenta que sanitiza.
Queries dinâmicas ou chamadas não parametrizadas sem o "scape" que saiba o contexto direto, superperigosas! E usado pelo interpretador, mais perigosas ainda! Então, interpretador aqui, eu estou comentando de SQL, mas poderia ser de outras coisas. Eu vou citar outras coisas, sim.
Então vem a informação do usuário, que precisa validar e sanitizar antes de utilizar, sempre. Então dados hostis são usados em queries de parâmetros. Também pode acontecer coisas do gênero: a pessoa também pode fazer outras queries em que ela vai tentar descobrir coisas.
Imagine que eu tenho aqui uma busca – aqui no meu caso, eu não tenho, mas eu tenho uma busca que busca usuários, por exemplo. No "buscaUsuarios" eu tenho lá quando eu acesso ela, ele procura: "nome=Guilherme". Imagine que tem um "nome=Guilherme" e eu percebi esse padrão. O que eu faço?
Eu, usuário aqui testando a segurança do sistema, posso adicionar um parâmetro a mais. Eu coloco "admin=true" e vejo o que acontece, isto é, eu vou ver se os parâmetros que estão sendo utilizados extraem informações sensitivas aqui para nós, isto é, informações. Então eu estou descobrindo quem é "admin" só passando um parâmetro novo.
Claro, se o meu servidor estiver lendo esses parâmetros dinamicamente, seja aqui, seja de outra forma... Abriu o buraco para brecha, de novo! Por quê? Porque eu não validei o que eu aceitava e o que eu não aceitava.
Dados hostis são utilizados ou concatenados de forma que as suas queries dinâmicas, comandos, "stored procedures" etc. suportem coisas que você não queria no SQL. Então, perceba que basicamente todo mundo que tem SQL está com o buraco de "injection", a não ser que você cuide, não concatene "strings".
Mas não é só SQL, NoSQL também. Comandos no sistema operacional, você vai dar "exec" em alguma coisa que o usuário passou, buraco! Por quê? Porque imagine aqui que você vai dar um "exec" aqui e o "exec" que você vai executar em um sistema operacional é um comando que a pessoa passa aqui, é um parâmetro que a pessoa passa aqui. O que a pessoa faz? Injeta o que ela quiser!
Ela faz o "scape" e faz um ponto e vírgula para determinar o último comando "ls" e ela lista todos os arquivos de um diretório, por exemplo - dando um exemplo simples. Então você também não pode confiar somente no SQL ou NoSQL. Em qualquer coisa que o usuário final passou a informação para nós, nós temos que validar, temos que sanitizar antes de utilizar.
Então ele dá vários exemplos aqui, porque ele está tentando deixar claro que: "olhe, não é um problema de SQL não, não vai usar NoSQL achando que não tem esse problema. Não! Esse problema é em qualquer lugar que nós usamos dados informados pelos usuários finais".
O problema é o mesmo. A revisão de código-fonte é a melhor maneira para detectarmos se elas estão vulneráveis, então nós precisamos ficar olhando. É claro, tem outras maneiras automatizadas de você fazer isso.
Você pode fazer uma busca dinâmica ou estática de código. Então você pode usar ferramentas, aqui tem SAST e DAST de estático e dinâmico. Então, o estático aqui e dinâmico aqui. Derramentas que podem passar pelo nosso código procurando buracos do gênero, concatenações etc. Por exemplo: para Python, o Bandit e tem vários outros, PMD para Java, etc., vários outros que vão procurar esses buracos.
Um exemplo de ataque, eu citei já o exemplo do SQL, mostrei o exemplo, aqui tem um outro exemplo de concatenação também aqui nesse caso com Java e SQL puro, mas tem um exemplo com Hibernate, sem ser com SQL, com Query language, HQL. Por quê? Concatenou? Boa sorte!
E ele dá aqui o exemplo dos dois casos de usar aspas simples etc. para quebrar a segurança nossa aqui. Como eu posso me prevenir? Essa é uma grande questão e nós vamos falar disso daqui a pouquinho!
OWASP Top Ten e Injection - Prevenção de injeção
Vamos voltar no PDF. Nós vimos que quando uma aplicação é vulnerável, basicamente se ela utiliza informações que o usuário final passa para ela, para fazermos qualquer tipo de coisa. Temos que dar uma validada antes na informação.
Nós vimos isso, SQL é só um dos exemplos. Por que ele é tão famoso? Porque está em todo lugar. Se você utiliza NoSQL, vai ter o mesmo tipo de problema. Se você executa coisas no sistema operacional, vai ter o mesmo tipo de problema. Se você interpreta código dinamicamente, vai ter o mesmo tipo de problema. Então sempre tome cuidado com injeção, por isso que é o primeiro lugar. Não é à toa!
E como eu posso me prevenir? Uma maneira é localizar. Aqui você está diagnosticando e encontrando problemas. Isso são aquelas ferramentas que eu citei, tem ferramentas que analisam o código estaticamente, então ele olha o teu código estático parado no repositório.
Então você baixa o PMD, roda dentro do Maven do Java, ou você roda o seu Bandit dentro do seu "Continuous Integration", e ele fala: "olhe, aqui tem esse tipo de problema, ali tem aquele outro tipo de problema. Por favor, corrija". É uma maravilha utilizar essas ferramentas.
Mas, como eu corrijo? Para o que, eu troco, se eu não vou usar concatenação de "String"? O que eu faço? A maneira preferível é usar uma API segura. Não é eu, não é você quem vai sanitizar manualmente. Por quê? Porque nós vamos deixar brechas. Tem pessoas que já fizeram isso por nós, por anos etc. então tem ferramentas que já fazem isso por nós.
Então usar uma API segura, ao invés de concatenar todas essas ferramentas que fornecem uma variação da função em que você passa os parâmetros e ela faz a sanitização por nós. Então, por exemplo: no Java, bem básico, bem baixo, nivelou quase bem baixo o nível. Você tem o "PreparedStatement", então no "Java PreparedStatement" você vai ter um exemplo de "query statement" aqui. Super antiga a versão de Java, mas vai ser o suficiente.
Em que você fala a tua query... Então a query está aqui, meu SQL está aqui, "update" e você coloca ponto de interrogação. Você não faz concatenação nenhuma, você fala: "olhe, o primeiro parâmetro é esse e o segundo parâmetro é esse". Nesse exemplo aqui são dois números, mas se fosse uma "string" a própria API, iria fazer por nós, a sanitização. Então nós não iriámos precisar sanitizarmos.
Então é muito mais seguro, então não concatenamos "strings", nós utilizamos API que nos traz a segurança. Essa é uma das maneiras. Cadê? Está no arquivo PDF, está aqui uma das maneiras. Então, evitar utilizar o interpretador. Com o interpretador também em si. Então, se você vai usar o interpretador que interpreta aquele código... Não! Acalma-se, primeiro sanitize, faça alguma outra coisa. Se é que você vai precisar de um interpretador.
E ele cita sobre usar ferramentas ORM. O Hibernate é uma dessas ferramentas ORMs então o próprio Hibernate tem o HQL, que é o Hibernate Query Language. Então vou pegar aqui esse exemplo mesmo do próprio JBoss, que são os mantenedores do Hibernate.
E aqui nós temos, por exemplo, "from" não sei o que, não sei o que lá, não sei o que lá. Mas aqui, de novo, é tudo do mesmo risco. Estou colocando todo o texto em uma "string" enorme. Boa sorte! Se eu colocar todo o texto aqui em uma "string" vai ser boa sorte para nós. Por quê? Porque nós temos o problema de sanitização.
Então, o que nós temos que fazer? Vamos ter que, de alguma maneira, em algum lugar aqui, ver se ele nos passa aqui componentes. Aqui ele não passou, aqui ele está em inglês. Vamos tentar pegar aqui a referência em inglês, clicamos "html single/", pesquisamos "query". "Query Language Substitution", "Querying", "Executgin queries", executando queries.
Então aqui ele tem uma query que ele está executando, tornando diversos resultados e colocando parâmetros. Ele fala: "olhe, para colocar parâmetros, primeiro crie uma query e depois você coloca o parâmetro".
Por quê? Porquê o próprio Hibernate vai fazer a sanitização por nós, quer dizer, é equivalente ao JDBC. Tanto o JDBC, quanto o Hbernate tem a maneira de concatenar "string" e não concatenar "string".
É o que eu falei: toda API descente vai ter uma maneira de simplesmente concatenar a "string", porque é só você passar a "string", mas vai apresentar uma maneira de sanitizar os dados para você, seja ORM ou não.
Então ele até cita, mesmo parametrizado. Aqui ele cita do ORM? Não, ele não citou aqui, mas mesmo ORM pode ter buraco, mesmo a ferramenta pode ter buracos se você continuar concatenando. Não é só estou usando Hibernate, estou usando [GRAPHIC NEW FORGEY]. Não é o suficiente, não podem estar concatenando "strings".
E mesmo parametrizado da maneira que nos estamos vendo, tem que tomar cuidado, porque se de repente alguma das "strings" que você passa para o seu banco, o seu banco dá um "execute" por trás ou o seu banco executa sem dar "escape" alguma coisa também; você abre um buraco novo. Por quê? Porque você está passando coisas que vão ser utilizadas e interpretadas do outro lado.
Sempre tome cuidado, seja quem for. Você está em um componente da tua aplicação, que recebeu de um outro componente alguma coisa para executar. Pergunte-se: você confia ou tem que sanitizar?
Não importa se quem fez esse outro componente é uma outra pessoa da sua equipe. Não digo assim direto da sua equipe, mas o que eu digo é: beleza, é um outro micro serviço da tua aplicação, não sei o quê. Você tem que assumir que pode ter chegado alguma coisa ali que quebra a sua segurança e você tem que tomar cuidado! Ou não? Que se exploda? Em geral nós temos que tomar cuidado e sanitizarmos, seja onde for.
Então aqui eu estou citando que um "stored procedures" em um banco seria um outro componente que tomaria cuidado de novo. Use listas positivas (o termo antigo era "whitelists"), mas use listas positivas para validar os dados.
Então, quer dizer, se você tem parâmetros que estão sendo passados aqui... Por exemplo: eu quero criar um usuário, "createUser?username" igual a tal coisa e "password" igual a tal coisa. Quando você for criar um usuário no Server-side, somente leia os parâmetros da sua lista positiva, que é a lista que você falou que tem interesse, que é o "password" e "username".
Então você passa para o seu framework de alguma maneira, ou você lê ativamente, somente os parâmetros da lista. Por quê? Porque se alguém passar aqui um "admin=true", você vai ignorar. Se você fizer isso de uma maneira... Não! Não aceite tudo e ponto final! Você vai aceitar o a"admin=true". O que vai criar? Vai criar um usuário que é administrador, que você não queria criar.
Então use listas positivas, listas que dizem quais são os parâmetros que eu tenho interesse de ler, ou que ativamente busque os seus parâmetros. Uma das duas coisas. Não é uma defesa completa, porque temos os mesmos tipos de problema de injeção ainda, mas use, mesmo assim use!
Para qualquer query dinâmica que ficou, se ficou alguma última que você não conseguiu uma dessas soluções, faça o "scape"! E tem os cuidados, por exemplo: algumas estruturas de SQL, mas de outras coisas também podem ter. Não tem como ou é difícil de fazer o "scape". Em geral é difícil em todas.
Por fim, use o "limit". Lembra que eu falei do "limit"? Por que usar o "limit"? Sempre use o "limit", se você sabe no login que você vai trazer só uma pessoa, faça o "limit 1. Por quê? Porque você traz só um usuário.
Se você não tem "limit" e a pessoa digita isso daqui, ela vai trazer todos os administradores, e ela consegue não só se logar como administradora, mas ela consegue ver tudo de uma vez só. Você não está nem dificultando os resultados.
Então sempre que possível, limite os resultados que você traz de um outro lado que você esteja se comunicando. Traga somente aquilo que você precisa. Então ele cita o "limit" para colocar nas queries do SQL ou similares, mesmo NoSQL etc.
Então coloque outros controles para diminuir a quantidade de dados, de "mass disclosure of records", mas não vaze tudo de uma vez só, coloque só aquilo que a pessoa pediu. Então aqui também, quando eu falei o "select*", não é asterisco. Se eu só quero o ID, é só o ID, então só o ID "limit 1". Coloque as restrições para evitar um vazamento completo.
Então essas são as prevenções. Claro, o fato de isso daqui ser feito rapidamente em um "select*", procurar query sem "limit"... Tem coisas que dão para serem feitas de forma estática. Até mesmo com as teclas "Ctrl +F", mas tem ferramentas, como eu citei, que já fazem isso para nós, de acordo com a linguagem que utilizamos.
É só baixar uma ferramenta que faz sentido, às vezes na sua ideia, às vezes no processo de integração contínua, e colocar no seu "build".
Além dessas paginas, tem muitas informações que nós podemos ver de "injection". Eu citei aqui "injection" principalmente com SQL, mas lembra que eu comentei que poderia ser na linha de comando? Poderia ser em qualquer lugar!
Então você pode encontrar muito mais informação ainda do "injection", se você seguir outras referências. Tem várias outras referências, por exemplo: "SQL injection no CWE", olhe no CWE, é um registro desses tipos de problemas, "Common Weakness Enumeration".
O 89 é "SQL Injection", então uma neutralização imprópria de elementos especiais. Ele dá os exemplos etc., olhe o exemplo do "delete": em vez de um "drop table", ele dá um "delete". Vários exemplos etc.
Mas tem também injeção de comandos, tem injeção com Hibernate, tem injeção com qualquer coisa. Qualquer coisa que você vai interpretar, vai gerar tipos de danos diferentes.
Você vai ter até outros dos problemas clássicos do OWASP, que não colocamos na categoria "Injection A1". Vão aparecer meio que como "injection" em outros lugares, porque é o usuário enviando coisas e nós não validando adequadamente.
Então, com "injection", o principal risco que nós temos é que em qualquer lugar que a pessoa nos envia dados ou parâmetros, pode ter problema de injeção. Tem vários exemplos de problema de injeção e o impacto é vazar qualquer tipo de dado, é um acesso inadequado. A pessoa acessando como se fosse outro usuário, qualquer coisa. O impacto é bem grande mesmo!
Então nós temos que tomar cuidado ao usar esse tipo de ferramenta. Então de acordo com a linguagem que você usa, use uma ferramenta diferente. Porém, o mais importante é: se você está concatenando "string" que o usuário enviou para você para fazer qualquer coisa, seja SQL, seja qualquer coisa; pare de fazer isso a partir de agora.
Sobre o curso OWASP Top 10: de Injections a Broken Access Control
O curso OWASP Top 10: de Injections a Broken Access Control possui 134 minutos de vídeos, em um total de 24 atividades. Gostou? Conheça nossos outros cursos de Segurança em DevOps, ou leia nossos artigos de DevOps.