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.
      Espero que todos estejam bem.
      Seguinte: Tenho um arquivo xml onde alguns campos estão com : (dois pontos), como o exemplo abaixo:
       
      <item>
      <title>
      d sa dsad sad sadasdas
      </title>
      <link>
      dsadas dsa sad asd as dsada
      </link>
      <pubDate>sadasdasdsa as</pubDate>
      <dc:creator>
      d sad sad sa ad as das
      </dc:creator>
      </item>
       
      Meu código:
       
      $link = "noticias.xml"; 
      $xml = simplexml_load_file($link); 
      foreach($xml -> channel as $ite) {     
           $titulo = $ite -> item->title;
           $urltitulo = $ite -> item->link;
           print $urltitulo = $ite -> item->dc:creator;
      } //fim do foreach
      ?>
       
      Esse campo dc:creator eu não consigo ler. Como faço?
       
      Agradeço quem puder me ajudar.
       
      Abs
       
       
    • Por First
      Olá a todos!
       
      Eu estou criando um sistema do zero mas estou encontnrando algumas dificuldades e não estou sabendo resolver, então vim recorrer ajuda de vocês.
      Aqui está todo o meu código: https://github.com/PauloJagata/aprendizado/
       
      Eu fiz um sistema de rotas mas só mostra o conteúdo da '/' não sei porque, quando eu tento acessar o register nada muda.
      E eu também quero que se não estiver liberado na rota mostra o erro de 404, mas quando eu tento acessar um link inválido, nada acontece.
      Alguém pode me ajudar com isso? E se tiver algumas sugestão para melhoria do código também estou aceitando.
       
       
      Desde já, obrigado.
    • Por landerbadi
      Olá pessoal, boa tarde
       
      Tenho uma tabela chamada "produtos" com os seguintes campos (id, produto) e outra tabela chamada "itens" com os seguintes campos (id, prod_01, prod_02, prod_03, prod_04).
       
      Na tabela produtos eu tenho cadastrado os seguintes produtos: laranja, maçã, uva, goiaba, arroz, feijão, macarrão, etc.
       
      Na tabela itens eu tenho cadastrado os itens da seguinte maneira:
       
      1, laranja, uva, arroz, feijão;
      2, maçã, macarrão, goiaba, uva;
      3, arroz, feijão, maçã, azeite
       
      Meu problema é o seguinte: 
      Eu escolho um produto da tabela "produtos", por exemplo "uva".  Preciso fazer uma consulta na tabela "itens" para ser listado todos os registros que contenham o produto "uva" e que todos os demais produtos estejam cadastrados na tabela "produtos".
       
      No exemplo acima seria listado apenas dois registros, pois o terceiro registro não contém o produto "uva". 
       
      Alguém pode me ajudar? Pois estou quebrando a cabeça a vários dias e não consigo achar uma solução.
×

Informação importante

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