Solucionado (ver solução)
Solucionado
(ver solução)
5
respostas

Segurança além da $_SESSION

Olá pessoal, gostaria de abrir um outro ponto sobre a segurança que vimos no sistema neste curso. No caso do curso, você vai ali e cria uma função de segurança que valida a $_SESSION["email"] que é criada ao logar. Porém, digamos que tenhamos uma outra situação:

"Um projeto onde todos os usuários podem gravar e consultar no banco de dados ( digamos um projeto de blog de noticias onde todos usuários cadastrados podem efetuar ações no mysql )."

O problema é que todos os usuários terão a $_SESSION["email"] ativa, pois todos estão logados ! Então, eu poderia por exemplo no botão de deletar meu post, passar um id_noticia que não seria o meu e dessa forma eu iria deletar o conteúdo de outro usuário. Isso serve também para gravar no banco, selecionar noticias que não seriam minhas, etc.

Como proceder então dessa forma ? A minha ideia seria criar uma hash de segurança extensa(criptografia) e guardar no banco como campo "token_segurança". Todas as tabelas do banco, devem ter esta coluna "token_seguranca". Ao logar, eu busco essa hash e salvo nas minhas sessions e TODA e qualquer sql que vai para o banco, antes da ação, bate nessa validação e verifica se é o mesmo token e daí sim executa a função.

Seria essa a forma ideal ? O problema disso é que toda vez que for fazer qualquer ação no banco, primeiramente eu terei que fazer um select nessa tabela e consultar esse token para ver se bate, caso contrário retorna função de proibido.

Outra questão: por mais que eu envie um input hidden na ação de editar um formulário, eu posso pelo html mudar o id dele e enviar junto. Mesmo com o parâmetro readonly="true", eu mudo para false e também consigo alterar o value passando para a ação do form. Alguém possui uma ideia ?

Abraço e desculpa pelo textão hehe.

5 respostas

Vou responder pelo final.

Algo que fazemos para resolver este tipo de problema referente aos formulários é criar um campo hidden com nome e conteúdo variável, baseados num valor randômico gerado em sessão.

Ao receber o post, verificamos se o nome/conteúdo do hidden batem com as sessões que criamos para gerar este campo. Se bater, prosseguimos.

Um exemplo seria este:

https://css-tricks.com/serious-form-security/

No caso deste tutorial, o nome do campo hidden é fixo, mas você pode deixar randômico.

Desta forma, cada form tem um ID único, o que diminui riscos de segurança.

Sobre a questão inicial, realmente é um problema você ter algo assim:

http://localhost/meusite/minhapagina.php?apagar=10

Porque você vai ter esse problema que você relatou, ou seja, alguém pode tentar colocar outros números, e apagar coisas que não deve.

Neste caso, o que você comentou é valido. Neste exemplo, ao invés de passar o ID, você poderia gerar um HASH do id, o que acabaria gerando algo assim:

http://localhost/meusite/minhapagina.php?apagar=xyz7812asdf12

Desta forma o usuário mal intencionado não vai saber quais os códigos válidos.

Você pode validar no back-end as ações do usuário. Por exemplo deixar ele apenas apagar notícias que pertencem a ele. Assim você pode exibir uma mensagem de permissão negada pro usuário caso ele tente apagar um notícia que não é dele.

Olá Daniel, dei uma olhada em todo o material do seu link e achei muito interessante !! Estava procurando um artigo completo como este, vamos ver se entendi todos os pontos:

  • function generateFormToken($form);

Ao entrar em uma página com formulário, ele gera este token e coloca no value do input hidden. Ao enviar este formulario, ele vai verificar se existe a sessao criada pela função, depois vai verificar se existe um post token e após isso ele ainda vai verificar se o valor informado é o mesmo da session. Seria isso ? É uma ótima função, mas para conter ataques de quem não estaria logado no caso, isso ?! Porque senão ele teria todos estes dados ( não seria a questão do meu exemplo ).

Depois, tem no seu link lá a função:

  • function writeLog($where); Uma função que vai me enviar os dados de quem está tentando me invadir e em qual parte do sistema está tentando invadir. Caso não grave o no log, me envia um e-mail ( achei excelente para um controle inclusive do sistema).

  • $whitelist = array('token','req-name','req-email','typeOfChange','urgency','URL-main','addURLS', 'curText', 'newText', 'save-stuff');

Essa seria uma função para checar o name de todos os input se eu envio, isso ? Caso alguém tente enviar um campo que não esteja nesse array, ele quebra e retorna como hack. Mas, de que forma isso poderia me prejudicar? Se ele está me enviando um post que eu não vou executar em lugar alguma da função ?

  • Valid URL

Esta função, serve para verificar se a url que estão me enviando é uma URL válida, correto ? Mas de que forma eu poderia receber dados se não fosse de uma URl válida ? Como acontece este ataque?

  • Cleaning values

Estas são funções que estou procurando a tempos para evitar ataques de sql injection e ataque xss. Eu vi que você utiliza htmlentities(). Já ouvi falar também em htmlspecialchars(). Por que você preferiu usar a htmlentities ? Não se usa preg_match também ?

Outra questão: por que não usar uma dessas duas funções para todos os tipos de input ? function cleantohtml($s); function stripcleantohtml($s)

Não posso usar uma só para tudo ?

Você disse que é possível enviar um script pelo e-mail e rodar ele ao executar. Você poderia me mostrar um exemplo deste ataque via e-mail?

Por último: desenvolvo apps mobile há um bom tempo e uma das questões de segurança que penso é a seguinte: nós recebemos um post do usuário, executamos funções e retornamos. Porém, como não é uma ação do administrador web, não existe criação ou validação de session. Hoje, é possível abrir um projeto android e visualizar seu código por completo e neste caso, poderia enviar um post de url externa com script maligno. Não existe uma função onde eu poderia verificar se o endereço que está me enviando o post é um endereço do MEU domínio ? Neste caso, se alguém me enviasse um post de 'www.hacker.com/form.php', mesmo que tivesse na action a minha url e não ouvessem session, ele não conseguiria executar. Existe isso ?

Eu baixei todos os seus arquivos, mas quando coloquei para rodar, mesmo enchendo de script os campos, ele apareceu como enviado com sucesso, está certo?

Desculpe por um milhão de perguntas, mas depois desses esclarecimentos, tenho certeza que terei um projeto MUITO mais seguro hehehe abraço

solução!

A idéia por traz deste campo hidden com nome e valor dinâmico é evitar que o POST seja feito de fora do seu site. Se eu pego um formulário HTML de um site, eu posso gravar em outro lugar e postar os dados de lá, Gerando esse campo dinâmico e validando com a sessão gerada pelo servidor antes de processar o POST, eu evito esse problema.

Uma alternativa para fazer a mesma coisa seria usar um captcha.

Isso ajuda nos casos onde a validação do formulário é feita apenas do lado do servidor. Por exemplo, você tem um formulário de contato e usa um Javascript para validar se os dados estão no formato correto na hora em que o usuário clica no botão de enviar. Se a validação é feita apenas do lado do cliente, e alguém consegue postar os dados direto para a página, você vai ter problemas. Uma boa prática é repetir as mesmas validações do lado do servidor antes de gravar.

Como você viu no link, existem várias formas de limpar os campos para evitar SQL injection e ataques xss. A forma de validar acaba dependendo do campo, você pode ter um campo onde o conteúdo é formatado, por exemplo um editor de noticias, neste caso você vai guardar o campo como HTML, você pode ter campos númericos, data, etc... então além de limpar os dados para evitar injection, o ideal também é validar se o campo tem uma informação com o tipo que você está esperando.

Claro que isso aumenta o trabalho, já que você tem que fazer as validações em dobro (do lado do cliente, para orientar o usuário a preencher corretamente e do lado do servidor para evitar ataques).

Aqui tem um link com exemplos disso

http://www.codingcage.com/2015/02/server-side-form-validations-using-php.html

O curso de segurança Web aqui do Alura mostra vários exemplos de como esses ataques funcionam, vale a pena dar uma olhada

https://cursos.alura.com.br/course/seguranca-web-vulnerabilidades-do-seu-sistema

Sobre os seus aplicativos, você precisa pensar numa forma de autenticar suas requisições.

Você poderia criar uma rotina de login que retorne um token, ou seja, um código criptografado. No PHP, um exemplo disso seria usar o md5(). Se a cada login você gerar a chave e guardar numa tabela de controle a chave gerada, a data e o usuário, vai poder usar essa tabela como base de cada nova requisição.

Se depois de logar ele vai pesquisar ou postar dados, você poderia pedir para ele enviar juntos dos dados um campo a mais com os dados do token. Se ele existir em seu controle, você atende a requisição. De qualquer forma, mesmo que o token seja válido, você vai ter que fazer todo esse trabalho de limpeza dos dados recebidos.

Para ficar mais seguro, você pode expirar o token a partir de um certo tempo, o que forçaria a pessoa a logar periodicamente.

Para você saber de onde está vindo a requisição pode usar o objeto global $_SERVER, ele tem várias informações úteis. Uma programação que uso para puxar o IP do requisitante é esta:

    $ip = "";
      $ip_remoto = "";
      if (isset($_SERVER['REMOTE_ADDR'])){
        $ip_remoto = $_SERVER['REMOTE_ADDR'];
      }
      if (!empty( $_SERVER['HTTP_CLIENT_IP'])) {
        $ip = $_SERVER['HTTP_CLIENT_IP'];
      } elseif( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'])) {
            //to check ip passed from proxy
            $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
      } else {
        $ip = $_SERVER['REMOTE_ADDR'];
      }

Perfeito, obrigado pelas dicas Daniel ! Abraço

Quer mergulhar em tecnologia e aprendizagem?

Receba a newsletter que o nosso CEO escreve pessoalmente, com insights do mercado de trabalho, ciência e desenvolvimento de software