Ir para conteúdo

Arquivado

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

montano

[Resolvido] Segurança em um sistema de login

Recommended Posts

Boa tarde,

 

Nunca fiz um sistema de login proprio, sempre peguei pronto da net. Entendendo como funciona, mas gostaria de algumas dicas.

 

Estou querendo usar senha MD5 mas eu acho que sem uma palavra-chave fica muito vulneravel a ataque brutal-force.

Estava pensando em como fazer para incluir uma palavra-chave dentro da senha do usuario e disso tudo gerar o MD5, houvi falar que isso ajuda muito a se proteger.

 

Uma outra duvida é sobre bloquear palavras como 'root'. ^^, quais sao esses caracteres especiais e palavras que tem que ter cuidado.

 

Por fim UTF-8 é mais seguro?

 

agradeço a ajuda, abraços

Compartilhar este post


Link para o post
Compartilhar em outros sites

Olha, sobre o brutal force, tem que bloquear de outra forma... Tipo, limitando a, tipo 5 tentativas do mesmo IP ou algumas coisas assim...

Sobre o MD5, você pode usar mais de uma criptografia... tipo, md5(sha1()), por exemplo.

 

Sobre o SQL Injection... Sugiro este tópico aqui... Vai te dar uma noção sobre o assunto.

http://forum.imasters.com.br/index.php?/topic/276729-seguranca-em-php/

 

Carlos Eduardo

Compartilhar este post


Link para o post
Compartilhar em outros sites

Tome cuidado com a query, e evite que teu script falhe.

Erros mostrados na tela(warnings, notices), são ótimas brechas no sistema.

 

Uma 'dica' que acho legal, para sistemas de login, é verificar o usuário numa query separada da senha.

Se o usuário já for diferente dos que você tem no banco, já pode falhar a operação, e dizer que o cara errou 'algo', mas não informe oque.

 

Se o usuário existir, ai sim, você faz uma query, batendo user com senha.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Olha, sobre o brutal force, tem que bloquear de outra forma...

 

Bom, estava estudando hashs outro dia para implementar os 5 secure hash e durante as pesquisas acabei encontrando o seguinte:

 

Brute force:

Tando o hash de 160 bits (Sha1) quanto o de 128 bits (MD5) já foram quebrados, porém não da forma que muitos pensam.

 

O método de força bruta conseguiu na verdade causar uma colisão, que é criar dois hashs iguais para dois conteúdos diferentes. Isso significa que, independente da quantidade de tentativas que a pessoa fizer, elas só serão eficientes se ela tiver acesso ao hash que está armazenado no seu banco de dados.

 

O grande problema para esses dois hashs é que eles são utilizados para assinar certificados digitais e o ataque foi exatamente em um certificado, onde um grupo de pesquisadores conseguiu criar uma assinatura válida para um certificado digital inválido.

 

Agora, um ponto a se levar em consideração é o que você está desenvolvendo; Se o sistema em questão for um site simples com uma área administrativa você deve apenas ter cuidado com injeção de SQL e de script (XSS) para evitar assim que se tenha acesso ao hash que está armazenado no banco de dados, e ainda assim, se alguém tiver acesso a essa informação ela só irá tentar conseguir a colisão se o conteúdo do seu site for realmente interessante para ela; isso porque, para se conseguir a colisão, o time precisou de 2**69 (2 elevado a 69 potência) operações, ou seja, é muito trabalho (e muito caro) conseguir uma colisão para um conteúdo que não vai trazer um grande retorno.

 

Um método que estou usando é o seguinte:

 

1. Crio uma tabela com os dados do usuário:

CREATE TABLE `users` (
   `id` int(8) NOT NULL AUTO_INCREMENT,
   `name` varchar(100) NOT NULL,
   `email` varchar(100) NOT NULL,
   `pswd` varchar(40) NOT NULL,
   `active` tinyint(1) unsigned NOT NULL DEFAULT '1',
   PRIMARY KEY (`id`),
   KEY `actives` (`active`) USING BTREE,
   KEY `login` (`active`,`pswd`) USING BTREE
) ENGINE=InnoDB;

 

Perceba que meu índice de login leva em consideração apenas a senha e se o usuário está ativo, isso porque para gravar a senha eu faço:

1. Extraio o hash do email: sha1( email )

2. Extraio o hash da senha que o usuário escolheu: sha1( senha )

3. Uso o algorítimo AES para criptografar o hash da senha usando como chave o hash do email

4. Extraio o hash da criptografia da senha

 

Porque extraio o hash do email e da senha ?

Simples, quando o usuário fizer um post com esses dados, mesmo que ele tente alguma coisa será infrutífero uma vez que:

 

senha = ' OR 1=1 OR ''='

hash da senha = be48104acb6aea64b431f10b5212367f41a0ecf0

 

Bom, depois de extrair o hash da criptografia da senha eu tenho o campo de senha com um valor que só será válido para aquela senha e aquele email, ai eu crio um procedimento no banco de dados:

 

delimiter $
create procedure `login`( p_email VARCHAR(40) , p_pswd VARCHAR(40) )
begin
   select
       `active`,
       `email`,
       `name`,
       `pswd`,
       count(*) "total"
   from
       `users` a
   where
       ( a.`active` = 1 ) AND
       ( a.`pswd` = sha1( aes_encrypt( p_pswd , p_email ) ) )
   having
       total=1;
end$
delimiter ;

 

Para testar ainda no banco de dados:

mysql> insert into `users`(`name`,`email`,`pswd`) values
    -> ('João Batista Neto','neto.joaobatista@gmail.com',sha1( aes_encrypt( sha1( 'minhasenha' ) , sha1( 'neto.joaobatista@gmail.com' ) ) ) );
Query OK, 1 row affected (0.00 sec)

mysql> select * from `users`;
+----+--------------------+----------------------------+------------------------------------------+--------+
| id | name               | email                      | pswd                                     | active |
+----+--------------------+----------------------------+------------------------------------------+--------+
|  1 | João Batista Neto | neto.joaobatista@gmail.com | f22a61cd66de0f7f8700e8a0aec6453f00d81643 |      1 |
+----+--------------------+----------------------------+------------------------------------------+--------+
1 row in set (0.00 sec)

Agora usando o procedimento:

mysql> call login( sha1( 'neto.joaobatista@gmail.com' ) , sha1( 'minhasenha' ) );
+--------+----------------------------+--------------------+------------------------------------------+-------+
| active | email                      | name               | pswd                                     | total |
+--------+----------------------------+--------------------+------------------------------------------+-------+
|      1 | neto.joaobatista@gmail.com | João Batista Neto | f22a61cd66de0f7f8700e8a0aec6453f00d81643 |     1 |
+--------+----------------------------+--------------------+------------------------------------------+-------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

Agora que a parte do banco de dados está funcionando, no PHP ficaria alguma coisa assim:

 

UserType.php

<?php
final class UserType {
    /**
     * Define se o usuário está ativo
     * @var integer
     */
    public $active = 0;

    /**
     * Email do usuário
     * @var string
     */
    public $email;

    /**
     * Nome do usuário
     * @var string
     */
    public $name;

    /**
     * Senha do usuário
     * @var unknown_type
     */
    public $pswd;

    /**
     * Define se o usuário está logado ou não
     * @var boolean
     */
    public $status = false;
}

Application.php

<?php
final class Application {
    /**
     * Servidor de banco de dados
     */
    const DB_HOST = "127.0.0.1";

    /**
     * Usuário do banco de dados
     */
    const DB_USER = "usuario";

    /**
     * Senha do usuário do banco de dados
     */
    const DB_PSWD = "senha";

    /**
     * Nome do banco de dados
     */
    const DB_NAME = "login";

    /**
     * Instância de PDO que será usado nos trabalhos com banco de dados
     * @var PDO
     */
    private $conn;

    /**
     * Faz o login de um usuário
     * @param string $email O email do usuário
     * @param string $pswd A senha do usuário
     * @return UserType Objeto do tipo UserType contendo os dados do usuário
     */
    public function login( $email , $pswd ){
        $ret = new UserType();

        $this->connect();

        $stat = $this->conn->prepare( "call login( :email , :pswd );" );
        $stat->setFetchMode( PDO::FETCH_INTO , $ret );
        $stat->bindParam( ":email" , sha1( $email ) , PDO::PARAM_STR );
        $stat->bindParam( ":pswd"  , sha1( $pswd  ) , PDO::PARAM_STR );
        $stat->execute();

        if ( $stat->fetch() ) $ret->status = true;

        $stat->closeCursor();

        return( $ret );
    }

    /**
     * Faz a conexão com o banco de dados
     */
    private function connect(){
        $this->conn = new PDO( sprintf( "mysql:host=%s;dbname=%s" , self::DB_HOST , self::DB_NAME ) , self::DB_USER , self::DB_PSWD );
    }
}

E a página que irá processar o login:

 

processa.php

require( "UserType.php" );
require( "Application.php" );

$email = isset( $_POST[ "email" ] ) ? $_POST[ "email" ] : "";
$senha = isset( $_POST[ "senha" ] ) ? $_POST[ "senha" ] : "";

try {
    $teste = new Application();

    var_dump( $teste->login( $email , $senha ) );
} catch( PDOException $e ){
    printf( "Opz, %s\n" , $e->getMessage() );
}

Se for enviado pelo formulário os dados corretos (neto.joaobatista@gmail.com e minhasenha) a saída disso será:

object(UserType)#2 (6) {
  ["active"]=>
  string(1) "1"
  ["email"]=>
  string(26) "neto.joaobatista@gmail.com"
  ["name"]=>
  string(18) "João Batista Neto"
  ["pswd"]=>
  string(40) "f22a61cd66de0f7f8700e8a0aec6453f00d81643"
  ["status"]=>
  bool(true)
  ["total"]=>
  string(1) "1"
}

Aqui vai o link falando da colisão no sha1 e do time que conseguiu: http://www.schneier.com/blog/archives/2005/02/sha1_broken.html

 

E mais alguns links relacionados:

http://www.justinshattuck.com/2007/01/18/mysql-injection-cheat-sheet/

http://csrc.nist.gov/groups/ST/toolkit/secure_hashing.html

Compartilhar este post


Link para o post
Compartilhar em outros sites

Caracas João Batista Neto , grande explicação, estou prestes a adicionar um sistema de login em um sistema que estou desenvolvendo e...irei usar sua dica. http://forum.imasters.com.br/public/style_emoticons/default/joia.gif

 

Valeu aí..; http://forum.imasters.com.br/public/style_emoticons/default/thumbsup.gif

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

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