php - qual - criptografia de senhas



Criptografia bidirecional: eu preciso armazenar senhas que podem ser recuperadas (6)

As senhas são para um dispositivo de hardware, portanto, a verificação de hashes está fora de questão

Eh? Eu não entendo Você quer dizer apenas que a senha deve ser recuperável?

Como outros disseram, a extensão mcrypt fornece acesso a muitas funções criptográficas - no entanto, você está convidando seus usuários a colocar todos os ovos em uma cesta - um que será potencialmente um alvo para os invasores - e se você nem sabe como começar a resolver o problema, então você está fazendo um desserviço aos seus usuários. Você não está em condições de entender como proteger os dados.

A maioria das vulnerabilidades de segurança ocorre não porque o algoritmo subjacente seja defeituoso ou inseguro, mas devido a problemas com a maneira como o algoritmo é usado no código do aplicativo.

Dito isto, é possível construir um sistema razoavelmente seguro.

Você deve considerar apenas a criptografia assimétrica se tiver um requisito para um usuário criar uma mensagem segura que seja legível por outro usuário (específico). A razão é que é computacionalmente caro. Se você quiser apenas fornecer um repositório para que os usuários insiram e recuperem seus próprios dados, a criptografia simétrica é adequada.

Se, no entanto, você armazenar a chave para descriptografar a mensagem no mesmo local que a mensagem criptografada (ou onde a mensagem criptografada está armazenada), o sistema não estará seguro. Use o mesmo token para autenticar o usuário quanto à chave de decodificação (ou, no caso de criptografia assimétrica, use o token como a passphrase da chave privada). Como você precisará armazenar o token no servidor em que a descriptografia ocorre pelo menos temporariamente, convém considerar o uso de um substrato de armazenamento de sessão não pesquisável ou a passagem do token diretamente para um daemon associado à sessão que armazenaria o token. token na memória e executar a descriptografia de mensagens sob demanda.

Estou criando um aplicativo que armazena senhas, que o usuário pode recuperar e ver. As senhas são para um dispositivo de hardware, portanto, a verificação contra hashes está fora de questão.

O que eu preciso saber é:

  1. Como faço para criptografar e descriptografar uma senha em PHP?

  2. Qual é o algoritmo mais seguro para criptografar as senhas?

  3. Onde posso guardar a chave privada?

  4. Em vez de armazenar a chave privada, é uma boa ideia exigir que os usuários insiram a chave privada sempre que precisarem de uma senha descriptografada? (Usuários deste aplicativo podem ser confiáveis)

  5. De que maneiras a senha pode ser roubada e descriptografada? Do que preciso estar ciente?


Answer #1
  1. A função PHP que você está procurando é o Mcrypt ( http://www.php.net/manual/en/intro.mcrypt.php ).

O exemplo do manual é ligeiramente editado para este exemplo):

<?php
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$key = "This is a very secret key";
$pass = "PasswordHere";
echo strlen($pass) . "\n";

$crypttext = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $pass, MCRYPT_MODE_ECB, $iv);
echo strlen($crypttext) . "\n";
?>

Você usaria mcrypt_decrypt para descriptografar sua senha.

  1. O melhor algorithm é bastante subjetivo - pergunte a 5 pessoas, obtenha 5 respostas. Pessoalmente, se o padrão (Blowfish) não for bom o suficiente para você, você provavelmente terá problemas maiores!

  2. Dado que é necessário pelo PHP para criptografar - não tenho certeza se você pode escondê-lo em qualquer lugar - bem-vindos comentários sobre isso. PHP padrão melhores práticas de codificação se aplicam, é claro!

  3. Dado que a chave de criptografia estará em seu código de qualquer maneira, não tenho certeza do que você vai ganhar, desde que o resto do seu aplicativo seja seguro.

  4. Obviamente, se a senha criptografada e a chave de criptografia forem roubadas, o game over.

Eu coloquei um piloto na minha resposta - eu não sou um especialista em criptografia PHP, mas, eu acho que o que eu tenho respondido é uma prática padrão - saúdo comentários que outros possam ter.


Answer #2

Muitos usuários sugeriram usar o mcrypt ... o que é correto, mas eu gosto de dar um passo a mais para torná-lo facilmente armazenado e transferido (como às vezes valores criptografados podem torná-los difíceis de enviar usando outras tecnologias como curl ou json) .

Depois de ter criptografado com sucesso usando mcrypt, execute-o através de base64_encode e, em seguida, converta-o em código hexadecimal. Uma vez em código hexadecimal é fácil transferir de várias formas.

$td = mcrypt_module_open('tripledes', '', 'ecb', '');
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
$key = substr("SUPERSECRETKEY",0,mcrypt_enc_get_key_size($td));
mcrypt_generic_init($td, $key, $iv);
$encrypted = mcrypt_generic($td, $unencrypted);
$encrypted = $ua."||||".$iv;
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
$encrypted = base64_encode($encrypted);
$encrypted = array_shift(unpack('H*', $encrypted));

E do outro lado

$encrypted = pack('H*', $encrypted);
$encrypted = base64_decode($encrypted);
list($encrypted,$iv) = explode("||||",$encrypted,2);
$td = mcrypt_module_open('tripledes', '', 'ecb', '');
$key = substr("SUPERSECRETKEY",0,mcrypt_enc_get_key_size($td));
mcrypt_generic_init($td, $key, $iv);
$unencrypted = mdecrypt_generic($td, $encrypted);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);

Answer #3

Só sugeriria criptografia de chave pública se você quiser definir a senha de um usuário sem interação (isso pode ser útil para redefinições e senhas compartilhadas).

Chave pública

  1. A extensão OpenSSL , especificamente openssl_public_encrypt e openssl_private_decrypt
  2. Isso seria direto RSA supondo que suas senhas vão caber no preenchimento de tamanho de chave, caso contrário, você precisa de uma camada simétrica
  3. Armazenar ambas as chaves para cada usuário, a frase secreta da chave privada é a senha do aplicativo

Simétrico

  1. A extensão Mcrypt
  2. AES-256 é provavelmente uma aposta segura, mas esta poderia ser uma questão tão em si
  3. Você não - essa seria a senha do aplicativo

Ambos

4 Sim - os usuários teriam que inserir a senha do aplicativo sempre, mas armazená-la na sessão criaria outros problemas

5

  • Se alguém rouba os dados do aplicativo, ele é tão seguro quanto a cifra simétrica (para o esquema de chave pública, ele é usado para proteger a chave privada com a frase secreta).
  • Seu aplicativo deve definitivamente ser acessível apenas por SSL, preferencialmente usando certificados de cliente.
  • Considere adicionar um segundo fator para autenticação, que seria usado apenas uma vez por sessão, como um token enviado por SMS.

Answer #4

Como faço para criptografar e descriptografar uma senha em PHP? Implementando um dos muitos algoritmos de criptografia. (ou usando uma das muitas bibliotecas)

Qual é o algoritmo mais seguro para criptografar as senhas? Existem vários algoritmos diferentes, nenhum dos quais é 100% seguro. Mas muitos deles são seguros o suficiente para fins comerciais e até militares

Onde posso guardar a chave privada? Se você decidiu implementar o algoritmo de criptografia de chave pública (por exemplo, RSA), não armazena a chave privada. usuário tem chave privada. seu sistema tem chave pública que pode ser armazenada em qualquer lugar que você desejar.

Em vez de armazenar a chave privada, é uma boa ideia exigir que os usuários insiram a chave privada sempre que precisarem de uma senha descriptografada? (Usuários deste aplicativo podem ser confiáveis) Bem, se seu usuário pode lembrar números primos ridiculamente longos então - sim, porque não. Mas geralmente você precisaria criar o sistema que permitirá ao usuário armazenar sua chave em algum lugar.

De que maneiras a senha pode ser roubada e descriptografada? Do que preciso estar ciente? Isso depende do algoritmo usado. No entanto, sempre certifique-se de não enviar a senha não criptografada para ou do usuário. Criptografar / descriptografar no lado do cliente ou usar https (ou usar outro meio criptográfico para proteger a conexão entre o servidor e o cliente).

No entanto, se tudo que você precisa é armazenar as senhas de forma criptografada, sugiro que você use uma simples XOR Cipher. O principal problema deste algoritmo é que ele pode ser facilmente quebrado pela análise de freqüência. No entanto, como geralmente as senhas não são feitas a partir de parágrafos longos do texto em inglês, não acho que você deva se preocupar com isso. O segundo problema com a XOR Cipher é que, se você tiver uma mensagem criptografada e descriptografada, poderá descobrir facilmente a senha com a qual ela foi criptografada. Novamente, não é um grande problema no seu caso, pois afeta apenas o usuário que já foi comprometido por outros meios.


Answer #5

Pessoalmente, eu usaria mcrypt como os outros postados. Mas há muito mais a notar ...

  1. Como faço para criptografar e descriptografar uma senha em PHP?

    Veja abaixo uma classe forte que cuida de tudo para você:

  2. Qual é o algoritmo mais seguro para criptografar as senhas?

    mais seguro ? qualquer um deles. O método mais seguro, se você for criptografar, é proteger contra vulnerabilidades de divulgação de informações (XSS, inclusão remota, etc.). Se sair, o invasor pode eventualmente decifrar a criptografia (nenhuma criptografia é 100% não reversível sem a chave - Como @NullUserException indica que isso não é totalmente verdade. Existem alguns esquemas de criptografia que são impossíveis de decifrar, como o OneTimePad ) .

  3. Onde posso guardar a chave privada?

    O que eu faria é usar 3 chaves. Um é fornecido pelo usuário, um é específico da aplicação e o outro é específico do usuário (como um sal). A chave específica do aplicativo pode ser armazenada em qualquer lugar (em um arquivo de configuração fora da raiz da Web, em uma variável de ambiente, etc.). O usuário específico seria armazenado em uma coluna no banco de dados ao lado da senha criptografada. O usuário fornecido não seria armazenado. Então, você faria algo assim:

    $key = $userKey . $serverKey . $userSuppliedKey;

    O benefício lá é que quaisquer duas chaves podem ser comprometidas sem que os dados sejam comprometidos. Se houver um ataque SQL Injection, eles poderão obter o $userKey , mas não o outro 2. Se houver uma exploração de servidor local, eles poderão obter $userKey e $serverKey , mas não a terceira $userSuppliedKey . Se eles vão bater o usuário com uma chave, eles podem obter o $userSuppliedKey , mas não os outros 2 (mas, novamente, se o usuário for espancado com uma chave, você está muito atrasado).

  4. Em vez de armazenar a chave privada, é uma boa ideia exigir que os usuários insiram a chave privada sempre que precisarem de uma senha descriptografada? (Usuários deste aplicativo podem ser confiáveis)

    Absolutamente. Na verdade, essa é a única maneira de fazer isso. Caso contrário, você precisaria armazenar uma versão não criptografada em um formato de armazenamento durável (memória compartilhada, como APC ou memcached, ou em um arquivo de sessão). Isso está se expondo a compromissos adicionais. Nunca armazene a versão não criptografada da senha em nada, exceto uma variável local.

  5. De que maneiras a senha pode ser roubada e descriptografada? Do que preciso estar ciente?

    Qualquer forma de comprometimento de seus sistemas permitirá que eles visualizem dados criptografados. Se eles puderem injetar código ou acessar seu sistema de arquivos, poderão visualizar dados descriptografados (já que podem editar os arquivos que descriptografam os dados). Qualquer forma de replay ou ataque MITM também lhes dará acesso total às chaves envolvidas. Cheirar o tráfego HTTP bruto também lhes dará as chaves.

    Use SSL para todo o tráfego. E certifique-se de que nada no servidor tenha nenhum tipo de vulnerabilidade (CSRF, XSS, SQL Injection, Privilege Escalation, Execução Remota de Código, etc).

Edit: Aqui está uma implementação de classe PHP de um método de criptografia forte:

/**
 * A class to handle secure encryption and decryption of arbitrary data
 *
 * Note that this is not just straight encryption.  It also has a few other
 *  features in it to make the encrypted data far more secure.  Note that any
 *  other implementations used to decrypt data will have to do the same exact
 *  operations.  
 *
 * Security Benefits:
 *
 * - Uses Key stretching
 * - Hides the Initialization Vector
 * - Does HMAC verification of source data
 *
 */
class Encryption {

    /**
     * @var string $cipher The mcrypt cipher to use for this instance
     */
    protected $cipher = '';

    /**
     * @var int $mode The mcrypt cipher mode to use
     */
    protected $mode = '';

    /**
     * @var int $rounds The number of rounds to feed into PBKDF2 for key generation
     */
    protected $rounds = 100;

    /**
     * Constructor!
     *
     * @param string $cipher The MCRYPT_* cypher to use for this instance
     * @param int    $mode   The MCRYPT_MODE_* mode to use for this instance
     * @param int    $rounds The number of PBKDF2 rounds to do on the key
     */
    public function __construct($cipher, $mode, $rounds = 100) {
        $this->cipher = $cipher;
        $this->mode = $mode;
        $this->rounds = (int) $rounds;
    }

    /**
     * Decrypt the data with the provided key
     *
     * @param string $data The encrypted datat to decrypt
     * @param string $key  The key to use for decryption
     * 
     * @returns string|false The returned string if decryption is successful
     *                           false if it is not
     */
    public function decrypt($data, $key) {
        $salt = substr($data, 0, 128);
        $enc = substr($data, 128, -64);
        $mac = substr($data, -64);

        list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key);

        if (!hash_equals(hash_hmac('sha512', $enc, $macKey, true), $mac)) {
             return false;
        }

        $dec = mcrypt_decrypt($this->cipher, $cipherKey, $enc, $this->mode, $iv);

        $data = $this->unpad($dec);

        return $data;
    }

    /**
     * Encrypt the supplied data using the supplied key
     * 
     * @param string $data The data to encrypt
     * @param string $key  The key to encrypt with
     *
     * @returns string The encrypted data
     */
    public function encrypt($data, $key) {
        $salt = mcrypt_create_iv(128, MCRYPT_DEV_URANDOM);
        list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key);

        $data = $this->pad($data);

        $enc = mcrypt_encrypt($this->cipher, $cipherKey, $data, $this->mode, $iv);

        $mac = hash_hmac('sha512', $enc, $macKey, true);
        return $salt . $enc . $mac;
    }

    /**
     * Generates a set of keys given a random salt and a master key
     *
     * @param string $salt A random string to change the keys each encryption
     * @param string $key  The supplied key to encrypt with
     *
     * @returns array An array of keys (a cipher key, a mac key, and a IV)
     */
    protected function getKeys($salt, $key) {
        $ivSize = mcrypt_get_iv_size($this->cipher, $this->mode);
        $keySize = mcrypt_get_key_size($this->cipher, $this->mode);
        $length = 2 * $keySize + $ivSize;

        $key = $this->pbkdf2('sha512', $key, $salt, $this->rounds, $length);

        $cipherKey = substr($key, 0, $keySize);
        $macKey = substr($key, $keySize, $keySize);
        $iv = substr($key, 2 * $keySize);
        return array($cipherKey, $macKey, $iv);
    }

    /**
     * Stretch the key using the PBKDF2 algorithm
     *
     * @see http://en.wikipedia.org/wiki/PBKDF2
     *
     * @param string $algo   The algorithm to use
     * @param string $key    The key to stretch
     * @param string $salt   A random salt
     * @param int    $rounds The number of rounds to derive
     * @param int    $length The length of the output key
     *
     * @returns string The derived key.
     */
    protected function pbkdf2($algo, $key, $salt, $rounds, $length) {
        $size   = strlen(hash($algo, '', true));
        $len    = ceil($length / $size);
        $result = '';
        for ($i = 1; $i <= $len; $i++) {
            $tmp = hash_hmac($algo, $salt . pack('N', $i), $key, true);
            $res = $tmp;
            for ($j = 1; $j < $rounds; $j++) {
                 $tmp  = hash_hmac($algo, $tmp, $key, true);
                 $res ^= $tmp;
            }
            $result .= $res;
        }
        return substr($result, 0, $length);
    }

    protected function pad($data) {
        $length = mcrypt_get_block_size($this->cipher, $this->mode);
        $padAmount = $length - strlen($data) % $length;
        if ($padAmount == 0) {
            $padAmount = $length;
        }
        return $data . str_repeat(chr($padAmount), $padAmount);
    }

    protected function unpad($data) {
        $length = mcrypt_get_block_size($this->cipher, $this->mode);
        $last = ord($data[strlen($data) - 1]);
        if ($last > $length) return false;
        if (substr($data, -1 * $last) !== str_repeat(chr($last), $last)) {
            return false;
        }
        return substr($data, 0, -1 * $last);
    }
}

Note que estou usando uma função adicionada no PHP 5.6: hash_equals . Se você estiver abaixo de 5.6, você pode usar essa função substituta que implementa uma função de comparação segura usando a verificação dupla de HMAC :

function hash_equals($a, $b) {
    $key = mcrypt_create_iv(128, MCRYPT_DEV_URANDOM);
    return hash_hmac('sha512', $a, $key) === hash_hmac('sha512', $b, $key);
}

Uso:

$e = new Encryption(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$encryptedData = $e->encrypt($data, $key);

Então, para descriptografar:

$e2 = new Encryption(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$data = $e2->decrypt($encryptedData, $key);

Observe que usei $e2 pela segunda vez para mostrar que instâncias diferentes ainda descriptografam corretamente os dados.

Agora, como funciona / por que usá-lo em outra solução:

  1. Chaves

    • As chaves não são usadas diretamente. Em vez disso, a chave é esticada por uma derivação PBKDF2 padrão.

    • A chave usada para criptografia é exclusiva para cada bloco de texto criptografado. A chave fornecida, portanto, torna-se uma "chave mestra". Essa classe, portanto, fornece rotação de chave para chaves de criptografia e autenticação.

    • NOTA IMPORTANTE , o parâmetro $rounds é configurado para chaves aleatórias verdadeiras com força suficiente (128 bits de criptograficamente seguro aleatório no mínimo). Se você for usar uma senha, ou uma chave não aleatória (ou menos aleatória, em seguida, 128 bits de CS aleatórios), você deve aumentar esse parâmetro. Eu sugeriria um mínimo de 10000 para senhas (quanto mais você puder pagar, melhor, mas isso aumentará o tempo de execução) ...

  2. Integridade de dados

    • A versão atualizada usa ENCRYPT-THEN-MAC, que é um método muito melhor para garantir a autenticidade dos dados criptografados.
  3. Criptografia:

    • Ele usa o mcrypt para realmente executar a criptografia. Eu sugeriria usar MCRYPT_BLOWFISH ou MCRYPT_RIJNDAEL_128 cyphers e MCRYPT_MODE_CBC para o modo. Ele é forte o suficiente e ainda bastante rápido (um ciclo de criptografia e descriptografia demora cerca de meio segundo na minha máquina).

Agora, quanto ao ponto 3 da primeira lista, o que isso lhe daria é uma função como esta:

function makeKey($userKey, $serverKey, $userSuppliedKey) {
    $key = hash_hmac('sha512', $userKey, $serverKey);
    $key = hash_hmac('sha512', $key, $userSuppliedKey);
    return $key;
}

Você poderia esticá-lo na função makeKey() , mas como ele será estendido mais tarde, não há realmente um grande ponto para fazê-lo.

Quanto ao tamanho do armazenamento, isso depende do texto simples. O Blowfish usa um tamanho de bloco de 8 bytes, então você terá:

  • 16 bytes para o sal
  • 64 bytes para o hmac
  • comprimento dos dados
  • Preenchimento para que o comprimento dos dados% 8 == 0

Portanto, para uma fonte de dados de 16 caracteres, haverá 16 caracteres de dados a serem criptografados. Isso significa que o tamanho real dos dados criptografados é de 16 bytes devido ao preenchimento. Em seguida, adicione os 16 bytes para o sal e 64 bytes para o hmac e o tamanho total armazenado é de 96 bytes. Portanto, há, na melhor das hipóteses, uma sobrecarga de 80 caracteres e, na pior das hipóteses, uma sobrecarga de 87 caracteres ...

Espero que isso ajude ...

Nota: 12/11/12: Acabei de atualizar esta classe com um método de criptografia MUITO melhor, usando chaves derivadas melhores e corrigindo a geração MAC ...





passwords