Ir para conteúdo

POWERED BY:

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 violin101
      Caros amigos, saudações.
       
      Por favor, me permita tirar uma dúvida com os amigos.

      Tenho um Formulário onde o Usuário digita todos os Dados necessários.

      Minha dúvida:
      --> como faço após o usuário digitar os dados e salvar, o Sistema chamar uma Modal ou mensagem perguntando se deseja imprimir agora ?

      Grato,
       
      Cesar
    • Por Carcleo
      Tenho uma abela de usuarios e uma tabela de administradores e clientes.
      Gostaria de uma ajuda para implementar um cadastro
       
      users -> name, login, passord (pronta) admins -> user_id, registratiom, etc.. client -> user_id, registratiom, etc...
      Queria ajuda para extender de user as classes Admin e Client
      Olhem como estáAdmin
      <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Admin extends User {     use HasFactory;            protected $fillable = [         'name',         'email',         'password',         'registration'     ];      private string $registration;     public function create(         string $name,          string $email,          string $password,         string $registration     )     {         //parent::create(['name'=>$name, 'email'=>$email, 'password'=>$password]);         parent::$name = $name;         parent::$email = $email;         parent::$password = $password;         $this->registration = $registration;     } } User
      <?php namespace App\Models; // use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Database\Eloquent\Relations\BelongsToMany; class User extends Authenticatable {     /** @use HasFactory<\Database\Factories\UserFactory> */     use HasFactory, Notifiable;     static string $name;     static string $email;     static string $password;     /**      * The attributes that are mass assignable.      *      * @var list<string>      */     protected $fillable = [         'name',         'email',         'password',     ];          /**      * The attributes that should be hidden for serialization.      *      * @var list<string>      */     protected $hidden = [         'remember_token',     ];     /**      * Get the attributes that should be cast.      *      * @return array<string, string>      */     protected function casts(): array     {         return [             'email_verified_at' => 'datetime',             'password' => 'hashed',         ];     }          public function roles() : BelongsToMany {         return $this->belongsToMany(Role::class);     }       public function hasHole(Array $roleName): bool     {                 foreach ($this->roles as $role) {             if ($role->name === $roleName) {                 return true;             }         }         return false;     }         public function hasHoles(Array $rolesName): bool     {                 foreach ($this->roles as $role) {             foreach ($rolesName as $rolee) {             if ($role->name === $rolee) {                 return true;             }          }         }         return false;     }         public function hasAbility(string $ability): bool     {         foreach ($this->roles as $role) {             if ($role->abilities->contains('name', $ability)) {                 return true;             }         }         return false;     }     } Como gravar um Admin na tabela admins sendo que ele é um User por extensão?
      Tentei assim mas é claro que está errado...
      public function store(Request $request, Admin $adminModel) {         $dados = $request->validate([             "name" => "required",             "email" => "required|email",             "password" => "required",             "registration" => "required"         ]);         $dados["password"] =  Hash::make($dados["password"]);                  $admin = Admin::where("registration",  $dados["registration"])->first();                  if ($admin)              return                    redirect()->route("admin.new")                             ->withErrors([                                 'fail' => 'Administrador já cadastrados<br>, favor verificar!'                   ]);                            $newAdmin = $adminModel->create(                                    $dados['name'],                                    $dados['email'],                                    $dados['password'],                                    $dados['registration']                                 );         dd($newAdmin);         $adminModel->save();         //$adminModel::create($admin);                  return redirect()->route("admin.new")->with("success",'Cadastrado com sucesso');     }  
    • Por violin101
      Caros amigos, saudações.
       
      Gostaria de tirar uma dúvida com os amigos, referente a PDV.
       
      Estou escrevendo um Sistema com Ponto de Vendas, a minha dúvida é o seguinte, referente ao procedimento mais correto.

      Conforme o caixa vai efetuando a venda, o Sistema de PDV já realiza:
      a baixa direto dos produtos no estoque
      ou
      somente após concretizar a venda o sistema baixa os produtos do estoque ?
       
      Grato,
       
      Cesar
       
    • Por violin101
      Caros amigos do grupo, saudações e um feliz 2025.
       
      Estou com uma pequena dúvida referente a Teclas de Atalho.

      Quando o Caps Lock está ativado o Comando da Tecla de Atalho não funciona.
      ou seja:
      se estiver para letra minúscula ====> funciona
      se estiver para letra maiúscula ====> não funciona
       
      Como consigo evitar essa falha, tanto para Letra Maiúscula quanto Minúscula ?

      o Código está assim:
      document.addEventListener( 'keydown', evt => { if (!evt.ctrlKey || evt.key !== 'r' ) return;// Não é Ctrl+r, portanto interrompemos o script evt.preventDefault(); });  
      Grato,
       
      Cesar
    • Por ILR master
      Fala galera, tudo certo?
       
      Seguinte: No servidor A estou tentando fazer uma consulta com o servidor B, mas está dando erro.
      Estou usando o mesmo código de conexão do servidor B que funciona perfeitamente, mas no servidor A, dá erro.
      Segue código:
       
      $host = 'servidor B';
      $user = 'user';
      $pass = '********';
      $db   = 'banco';
       
      // conexão e seleção do banco de dados
      $conexao = mysqlI_connect($host, $user, $pass, $db);
      mysqlI_set_charset($conexao,"utf8");
      //print "Conexão rodando e OK!"; 
      //mysqlI_close($conexao);
       
      Alguém pode me ajudar?
×

Informação importante

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