Ir para conteúdo

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

AnthraxisBR

Existe alguma espécie de segurança nesse código?

Recommended Posts

Estou tentando desenvolver um sistema de login que seja seguro, aparentemente, acho que estou tendo sucesso, mas não sei como confirmar isso, então vim pedir ajuda de vocês, alguém poderia avaliar o código a baixo e me dizer se existe alguma espécie de segurança boa nele?

Desde ja obrigado.

 

Esse é a classe que trata os dados(não tratados com js) via método POST para o loginView renderizar as respostas.

 

<?php

class loginModel {
    //Verifica se o login foi efetuado, deve ser chamado em todas as paginas
    public function verif_if_is_logged() {
        //Nomeia a sessão
        session_name('system_access');
        //Inicia a sessão
        session_start();
        //Verifica se o ID do usuário ja foi definido
        if (!isset($_SESSION['userID'])) {
            //se não foi, destroi a sessão
            session_destroy();
            return 0;
        } else {
            //Se foi devolve true para o view
            return 1;
        }
    }
    // Função para verificar o login
    public function verif_if_is_valid_login() {
      //Função para filtrar os POSTS
        $_POST = Sanitize::filter($_POST);
        //pega os inputs com nomes encripitados
        $login_hash = sha1('user_login');
        $password_hash = sha1('user_password');
        //Pega os POSTS
        $user_login = $_POST[$login_hash];
        $user_pass = sha1($_POST[$password_hash]);
        //Query para verificar se o user existe
        $sql = "SELECT COUNT(*) FROM internal_users WHERE login = '$user_login' AND password = '$user_pass'";
        //Resposta da verificação de login
        $user_status = MySQLComponents::get_count_reg_mysql_PDO($sql);
        //Verifica se o usuário ja está logado
        $logged_status = self::verifi_if_user_is_logged($user_login, $user_pass);
        // se false, usuário ja está logado, recusar
        if ($logged_status['status'] == 0) {
            //resposta recusada
            $response['login_attempts']['status'] = 0;
            //mensagem da resposta
            $response['login_attempts']['msg'] = $logged_status['msg'];
        } else {
            //Se o usuário não está logado define o login como ativo
            $response = self::set_login_as_active($user_status, $user_login, $user_pass);
        }
        //Retorna para o ajax
        return $response;
    }
    //Função para verificar se o usuário ja está logado
    private function verifi_if_user_is_logged($user_login, $user_pass) {
        //Query quer verifica se o usuário está logado
        $sql = "SELECT COUNT(*) FROM internal_users WHERE login = '$user_login' AND password = '$user_pass' AND login_status = '1'";
        //REsposta da execução da query
        $login_status = MySQLComponents::get_count_reg_mysql_PDO($sql);
        //Se maior que 0, ja está logado
        if ($login_status > 0) {
            //Resposta de recusa
            $response['status'] = 0;
            //Mensagem de resposta
            $response['msg'] = 'O pedido de acesso foi negado pois este usuário ja encontra-se logado, foi enviado um notificação ao e-mail relacionado.';
        } else {
            //Resposta de aceito
            $response['status'] = 1;
        }
        //resposta para a função
        return $response;
    }
    //Função para definir o login como ativo
    private function set_login_as_active($login_status, $user_login, $user_pass) {
        //Se o login teve sucesso
        if ($login_status == 1) {
            //Inicia sessão nomeada
            session_name('system_access');
            session_start();
            //Query para buscar os dados do usuário
            $sql = "SELECT id,cod,level,relationship FROM internal_users WHERE login = '$user_login' AND password = '$user_pass'";
            //Resposta com os dados do usuário
            $rs = MySQLComponents::get_rs_array_stmt_PDO($sql);
            //Define os dados de usuário para a sessão
            $session_data = self::set_session_variables($rs);
            //Recura o COD do usuário
            $cod = $session_data['userCod'];
            //Verifica se a seessão foi iniciada e login foi feito
            $login_status = self::verif_if_is_logged();
            //Se o login foi feiro
            if ($login_status == 1) {
                //query para definir com logged true
                $sql_2 = "UPDATE internal_users SET login_status = '1' WHERE cod = '$cod'";
                //Resposta de query
                $rs_2 = MySQLComponents::get_exec_query_stmt_PDO($sql_2);
                //Se o usuário foi setado como logado
                if ($rs_2 == 1) {
                    //Salvar na tabela histório a ação de sucesso no login
                    historicMaker::set_data_on_history($cod, 'login_success', $cod);
                }else{
                    //Se não foi setado como ativo, salvar histórico de erro
                    historicMaker::set_data_on_history($cod, 'login_failed', $cod);
                }
            }
            //Resposta da siituação
            $response['status'] = $login_status;
        } else {
            //Gera um código para a tentativa de login
            $cod = uniqid(rand(), true);
            //Chama a função componente que pega o IP do usuário
            $IP = Components::get_client_IP();
            //Query para inserir tentativa de login
            $sql = "INSERT INTO internal_login_attempts (cod,user_relacionated,ip) VALUES ('$cod','$user_login','$IP')";
            //Resposta da query
            $rs = MySQLComponents::get_exec_query_stmt_PDO($sql);
            //Função que monta array com retorno das tentativas de login
            $response['login_attempts'] = self::verif_login_attempts($user_login);
            //Respara para false
            $response['status'] = 0;
        }
        //retorno da função
        return $response;
    }
    //Função para controlar as tentativas de login
    private function verif_login_attempts($user_login) {
        //Pega data de hoje
        $today = date('Y-m-d H:i:s');
        //Converte em timestamp
        $timestamp = strtotime($today);
        //Diminui 1 dia do timestamp
        $timestamp2 = strtotime('-1 day', $timestamp);
        //Converte o timestamp em formato datetime
        $minus_24h = date('Y-m-d H:i:s', $timestamp2);
        //Captura o IP do usuáio
        $IP = Components::get_client_IP();
        //Query para verificar a qeuantidade de tentativas nas ultimas 24 horas
        $sql = "SELECT datetime FROM internal_login_attempts WHERE IP = '$IP' AND (datetime < '$today' AND datetime > '$minus_24h')";
        //Resposta da query
        $rs = MySQLComponents::get_rs_array_stmt_PDO($sql);
        //Contagem de tentativas
        $count = (int) count($rs);
        if ($count == 0) {
            $response['msg'] = "3 tentativas restantes";
        } elseif ($count == 1) {
            $response['msg'] = "2 tentativas restantes";
        } elseif ($count == 2) {
            $response['msg'] = "1 tentativa restantes";
            $response['remember_pass']['status'] = 1;
            $response['remember_pass']['msg'] = "O acesso para esta máquina será bloqueado por 24 horas no próximo erro, o desbloqueio só será possível em contato com o provedor do serviço, deseja tentar novamente?";
        } elseif (($count > 2)) {
            $response['msg'] = "Máquina bloqueada, contate o provedor do serviço.";
        }
        return $response;
    }
//Função para definir as variaveis de sessão para o login ativo
    private function set_session_variables($rs) {
        //
        $count = count($rs);
        if ($count == 1) {
            foreach ($rs as $row) {
                $_SESSION['userID'] = $row['id'];
                $relationship = $row['relationship'];
                //Query para buscar o colaborador ou o cliente que está relocionado ao usuário ativo
                $sql = "(SELECT personal_name FROM data_collaborators WHERE cod = '$relationship') 
                        UNION
                        (SELECT company_name FROM data_companies WHERE cod = '$relationship') 
                        ";
                //Função compenente para executar a query
                $rs = MySQLComponents::get_rs_array_stmt_PDO($sql);
                //definindo as váriaveis
                $_SESSION['userName'] = $rs[0]['personal_name'] . $rs[0]['company_name'];
                $_SESSION['userLevel'] = $row['level'];
                $_SESSION['userCod'] = $row['cod'];
            }
        }
        return $_SESSION;
    }
    public function set_login_as_inactive() {
        //a fazer
    }
}

 

 

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Seu código impõe uma certa segurança, mas acaba pecando em alguns aspectos.

 

Não entenda que seu código possui ou não segurança. Ele possui uma segurança básica, mas, quando a exigência é sair do básico, está fragilizado.

 

Olhando em ordem pelo que vi no seu código:

 

Sessões

Uma leitura bem importante é sobre a segurança de sessões : Sessions and Security

 

SQL Injection

Praticamente não precisaia verificar a sua classe Sanitize para verificar que ela é vulnerável. Existe muito além de sanitizar para evitar SQL Injection. Mesmo sanitizando completamente, no momento que você interpola uma consulta SQL como uma string, seu código se torna vulnerável.

 

Referências:

https://phpdelusions.net/sql_injection

 

Inputs

o código abaixo é desnecessário

$login_hash = sha1('user_login');
$password_hash = sha1('user_password');

Quem tiver acesso ao HTML verá que o name do input de login e senha são "diferentes", ou seja, são hashs. Tanto que são disponíveis ao usuário. Só acaba gerando mais processamento.

 

Senha e hash

 

Sha1 sim é inseguro.

$user_pass = sha1($_POST[$password_hash]);

Tudo que foi dito no tópico abaixo sobre o md5 se aplica ao sha1:

Foi linkado no artigo acima, mas vale a pena linká-lo novamente. It's All About Time é um dos melhores artigos explicativos sobre como são realizadas as quebras de senha através de ataques de tempo (timing attack).

 

Considerações finais

 

"Tudo depende do contexto" (essa frase é minha mesmo).  Você não precisa fazer tudo que é tipo de segurança, vai depender muito do foco do seu sistema. Por exemplo, um CMS não requer segurança extrema. Por outro lado, um sistema financeiro requer até que dados persistidos em SGBD sejam criptografados.

 

PS.:

Desde a thread do artigo acima, estou juntando material para fazer uma série de vídeos sobre segurança. Vejo que existe muito material em inglês, mas muito pouco em português. Acho que esse final de semana eu lanço uma parte e abro discussão sobre como melhorar a segurança de aplicativos e, talvez, mitigar algumas boas discussões sobre o que é ou não necessário.

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Opa, vou dar uma lida nesse conteúdo, to com o um problema de entender o que é proteção e o que é só bagunçar as coisas, estou muito de tramitar informações entre funções que no fim das contas não haver nenhum necessidade de tanto processamento.

 

Esse é um sistema login intranet, seu propósito é pra não deixar um funcionário entrar na conta do outro e ver o que ele fez, ou ver o salário, coisas do tipo, a parte na nuvem usa um token sms que eu não vou precisar mexer (amém kkk).

 

A respeito da classe Sanitize, é um outra dúvida se essa classe realmente serve de alguma coisa, a que eu uso não foi eu que fiz, achei num github gringo.

 

Ela veio com isso e outra função que chama essa apenas:

 

<?php
static protected function _doFilter($value, $mode) {
        switch ($mode) {
            case 'html':
                $value = strip_tags($value);
                $value = addslashes($value);
                $value = htmlspecialchars($value);
                break;
        
            case 'sql':
                $value = preg_replace(sql_regcase('/(from|select|insert|delete|where|drop table|show tables|#|\*| |\\\\)/'),'',$value);
                $value = trim($value);
                break;
        }
        return $value;
    }

 

Eu tentei enviar uns comando de sql injection e pelo que eu vi ela protege sim, mas isso me traz uma outra dúvida:

 

Existe algum software (mesmo que pago) para testar vulnerabilidades de SQL Injection ?

 

PS.:

 

Por fim, acerca de brutal force, como eu registro todas as tentativas de login, e bloqueio o IP caso passe de 3 tentativas dentro de 24 horas, ele está seguro contra esse tipo de ataque ?

Compartilhar este post


Link para o post
Compartilhar em outros sites
29 minutos atrás, AnthraxisBR disse:

Por fim, acerca de brutal force, como eu registro todas as tentativas de login, e bloqueio o IP caso passe de 3 tentativas dentro de 24 horas, ele está seguro contra esse tipo de ataque ?

 

Anteriormente, acabei escrevendo na pressa e esqueci de pontuar, mas a quetão de bloquear o acesso após três tentivas é muito importante e realmente protege. Nesse caso, "bola dentro".

29 minutos atrás, AnthraxisBR disse:

Opa, vou dar uma lida nesse conteúdo, to com o um problema de entender o que é proteção e o que é só bagunçar as coisas, estou muito de tramitar informações entre funções que no fim das contas não haver nenhum necessidade de tanto processamento.

 

Esse é um sistema login intranet, seu propósito é pra não deixar um funcionário entrar na conta do outro e ver o que ele fez, ou ver o salário, coisas do tipo, a parte na nuvem usa um token sms que eu não vou precisar mexer (amém kkk).

 

 

Na programação, 99.9% do desenvolvimento pode ser orientado com a expressão "menos é mais". Exceto a segurança. Ela se baseia em outra regra, que eu denomino "balanço". O balanço seria entre segurança X velocidade (ou, UX, User eXperience). Se o seu sistema for o estado da arte em segurança, ele será lento. Se for rápido demais, vai ser frágil. Tanto é que, as funções hash_equals e password_verify são lentas, para justamente evitar a força bruta. Além de serem lentas, elas possuem alguma proteção contra timing attack.

 

29 minutos atrás, AnthraxisBR disse:

A respeito da classe Sanitize, é um outra dúvida se essa classe realmente serve de alguma coisa, a que eu uso não foi eu que fiz, achei num github gringo.

 

 

Ela era como eu imaginava. Você pode substituir pela PDO ou MySQLi que terá um proteção superior.

Compartilhar este post


Link para o post
Compartilhar em outros sites

  • Conteúdo Similar

    • Por ILR master
      Fala galera, tudo bem?
       
      Tenho o seguinte codigo:
       
       class Data {
      public static function ExibirTempoDecorrido($date)
      {
          if(empty($date))
          {
              return "Informe a data";
          }
          $periodos = array("segundo", "minuto", "hora", "dia", "semana", "mês", "ano", "década");
          $duracao = array("60","60","24","7","4.35","12","10");
          $agora = time();
          $unix_data = strtotime($date);
          // check validity of date
          if(empty($unix_data))
          {  
              return "Bad date";
          }
          // is it future date or past date
          if($agora > $unix_data) 
          {  
              $diferenca     = $agora - $unix_data;
              $tempo         = "atrás";
          } 
          else 
          {
              $diferenca     = $unix_data - $agora;
              $tempo         = "agora";
          }
          for($j = 0; $diferenca >= $duracao[$j] && $j < count($duracao)-1; $j++) 
          {
              $diferenca /= $duracao[$j];
          }
          $diferenca = round($diferenca);
          if($diferenca != 1) 
          {
              $periodos[$j].= "s";
          }
          return "$diferenca $periodos[$j] {$tempo}";
      }
      }
       
      Funciona redondinho se o valor retornado for de algumas horas, mas...
      Quando passa de dois meses, ele retorna a palavra mess. Deve ser por conta dessa linha
      if($diferenca != 1) 
          {
              $periodos[$j].= "s";
          }
       
      Quero que modre:
       
      2 meses atrás
      e não
      2 mess atrás.
       
      Espero que tenham entendido.
       
      Valeu
    • Por Carlos Web Soluções Web
      Olá...
      Estou tentando fazer o seguinte !!
      Listando dados em tabela !!
      Gostaria que....se na listagem houver 4 linhas...indepedente de seu número de ID, faça a listagem em ID ser em ordem 1 2 3 4 !!
      Exemplo...se tiver uma listagem de dados que está em ID 1 3 3...faça ficar 1 2 3 !!

       
      echo "<table class='tabela_dados' border='1'> <tr> <td>ID</td> <td>Nome Empresa</td> <td>Responsável</td> <td>Telefone 1</td> <td>Telefone 2</td> <td>E-mail 1</td> <td>E-mail 2</td> <td>Endereço</td> <td>CEP</td> <td>Bairro</td> <td>AÇÃO 1</td> <td>AÇÃO 2</td> </tr> "; $sql = "SELECT ID FROM usuarios_dados WHERE Usuario='$usuario'"; $result = $conn->query($sql); $num_rows = $result->num_rows; $Novo_ID = 1; for ($i = 0; $i < $num_rows; $i++) { $registro = $result -> fetch_row(); $sql2 = "UPDATE usuarios_dados SET ID='$Novo_ID' WHERE ID='$Novo_ID'"; $result2 = $conn->query($sql2); $Novo_ID++; } $sql = "SELECT * FROM usuarios_dados"; $result = $conn->query($sql); if ($result->num_rows > 0) { // output data of each row while($row = $result->fetch_assoc()) { echo "<tr> <td>$row[ID]</td> <td>$row[Nome_Empresa]</td> <td>$row[Responsavel]</td> <td>$row[Telefone_1]</td> <td>$row[Telefone_2]</td> <td>$row[Email_1]</td> <td>$row[Email_2]</td> <td>$row[Endereço]</td> <td>$row[CEP]</td> <td>$row[Bairro]</td> <td> <form method='post' action='Editar_Dados.php'> <input type='hidden' name='usuario' value='$usuario'> <input type='hidden' name='senha' value='$senha'> <input type='hidden' name='ID' value='$row[ID]'> <input type='submit' style='padding: 10px;' value='EDITAR'> </form> </td> <td> <form method='post' action='Deletar_Dados.php'> <input type='hidden' name='usuario' value='$usuario'> <input type='hidden' name='senha' value='$senha'> <input type='hidden' name='ID' value='$row[ID]'> <input type='submit' style='padding: 10px;' value='DELETAR'> </form> </td> </tr> "; } } else { echo "0 results"; } $conn->close();  
    • Por ILR master
      Boa tarde pessoal, tudo bem ?
       
      Eu uso o tinymce para cadastro de textos no meu siite, porém, quero fazer um sistema para que os colunistas possam fazer o próprio post.
      O problema do tinymce, é que ele mantém a formatação do texto copiado, como tamanho de fonts, negritos, etc... Quero que o usuário cole o texto e a própria textarea limpe a formatação para que ele formate como quiser.
       
      A pergunta é:
       
      O tinymce tem uma opção para desabilitar a formatação quando um texto é colocado?
      Tem alguma função via java ou php para retirar a formatação assim que o texto é colado?
      Ou é melhor usar um outro editor?
       
      Agradeço deste já.
    • Por Giovanird
      Olá a todos!
      Tenho uma pagina que possui uma DIV onde coloquei uma pagina PHP.
      Uso a função setInterval para atualizar a pagina inclusa dentro da DIV.
      O problema é que ao acessar o site , a DIV só me mostra a pagina inclusa somente quando completo o primeiro minuto.
      Preciso que a pagina inclusa já inicie carregada
       
      Meu código JavaScript e a DIV com a pagina PHP
       
      <script> function atualiza(){ var url = 'direita.php'; $.get(url, function(dataReturn) { $('#direita').html(dataReturn); }); } setInterval("atualiza()",60000); </script> <div> <span id="direita"></span> </div>  
    • Por Thiago Duarte
      Oi, gostaria de arrastar imagem e ao soltar formar bloco html, meu bloco de html ficaria com nome, content-1.html, content-2.html, etc
       
      Alguem pode me ajudar?
×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.