Como encriptar e descodificar um texto PHP?

O que quero dizer é:

Original String + Salt or Key --> Encrypted String
Encrypted String + Salt or Key --> Decrypted (Original String)
Talvez algo do género:
"hello world!" + "ABCD1234" --> Encrypt --> "2a2ffa8f13220befbe30819047e23b2c" (may be, for e.g)
"2a2ffa8f13220befbe30819047e23b2c" --> Decrypt with "ABCD1234" --> "hello world!"
    Em PHP, como podes fazer isto?

tentou usar Crypt_Blowfish, mas não funcionou comigo.

Author: Scott Arciszewski, 2013-05-17

6 answers

Antes de fazer mais nada, procurar compreender a diferença entre criptografia e autenticação, e por que você, provavelmente, quer authenticated encryption ao invés de incluir somente criptografia.

Para implementar a encriptação autenticada, deseja cifrar então o MAC. a ordem de encriptação e autenticação é muito importante!Uma das respostas existentes a esta pergunta cometeu este erro, tal como muitos outros. bibliotecas de criptografia escritas em PHP.

Você deve evitar implementar a sua própria criptografia, e em vez disso usar uma biblioteca segura escrita e revisada por especialistas em criptografia.

Actualização: o PHP 7.2 oferece agora o libsodium ! Atualizado para PHP 7.2 ou superior e siga apenas o conselho do libsódio nesta resposta.

Use o libsodium se tiver PECL access (ou sodium_ compat se quiser libsodium sem PECL); contrario...
Use a desactivação/PHP-encriptação ; Não role a sua própria criptografia!

Ambas as bibliotecas ligadas acima tornam fácil e indolor implementar encriptação autenticada nas suas próprias bibliotecas. Se você ainda quiser escrever e implantar sua própria biblioteca de criptografia, contra a sabedoria convencional de cada especialista em criptografia na Internet, estes são os passos que você teria que dar tomar.

Encriptação:

  1. encriptar usando o AES no modo CTR. Você também pode usar o GCM (que remove a necessidade de um MAC separado). Além disso, ChaCha20 e Salsa20 (fornecidos por libsodium) são cifras de fluxo e não precisam de modos especiais.
  2. a menos que tenha escolhido o GCM acima, deve autenticar o texto cifrado com HMAC-SHA-256 (ou, para as cifras de fluxo, Poly1305 -- a maioria das APIs libs do libsodium fazem isto por si). O MAC deve cobrir o IV, bem como o cifrotexto!

Descodificação:

  1. a menos que se utilize Poly1305 ou GCM, recalcular o MAC do texto cifrado e compará - lo com o MAC que foi enviado usando hash_equals(). Se falhar, abortar.
  2. Decifra a mensagem.

Outras Considerações De Concepção:

    Nunca comprimas nada. O texto cifrotexto não é compressível; comprimir o texto simples antes que a encriptação possa levar a fugas de informação (por exemplo, CRIME e violação em TLS).
  1. certifique-se de que utiliza mb_strlen() e mb_substr(), usando o modo '8bit' para evitar mbstring.func_overload problemas.
  2. os IVs devem estar a gerar usando um CSPRNG ; Se estiver a usar mcrypt_create_iv(), Não utilize MCRYPT_RAND!
  3. A não ser que estejas a usar uma construção hidromel, encripta sempre o MAC!
  4. bin2hex(), base64_encode(), etc. pode obter informações sobre as suas chaves de encriptação via tempo de cache. Evitá-los, se possível.
Mesmo que siga o conselho dado aqui, muita coisa pode correr mal com a criptografia. Tenha sempre um perito em criptografia a rever a sua implementação.Se não tiver a sorte de ser amigo pessoal de um estudante de criptografia na sua universidade local, pode sempre tentar o fórum para obter conselhos. Se precisar de uma análise profissional da sua implementação, pode sempre contratar um Uma equipa respeitável de consultores de segurança para rever o seu código de criptografia PHP (divulgação: o meu empregador).

Importante: quando não usar a encriptação

Não encriptar as senhas . Você quer hash em vez disso, usando um desses algoritmos de lavagem de senhas:

Nunca utilize uma função de hash de uso geral (MD5, SHA256) para armazenamento de senhas.

Não encriptar os parâmetros do URL.É a ferramenta errada para o trabalho.

Exemplo de encriptação de texto de PHP com o Libsodium

Se estiver a tomar PHP sodium_ compat

para alcançar o mesmo resultado (ainda que mais lento).
<?php
declare(strict_types=1);

/**
 * Encrypt a message
 * 
 * @param string $message - message to encrypt
 * @param string $key - encryption key
 * @return string
 * @throws RangeException
 */
function safeEncrypt(string $message, string $key): string
{
    if (mb_strlen($key, '8bit') !== SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
        throw new RangeException('Key is not the correct size (must be 32 bytes).');
    }
    $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);

    $cipher = base64_encode(
        $nonce.
        sodium_crypto_secretbox(
            $message,
            $nonce,
            $key
        )
    );
    sodium_memzero($message);
    sodium_memzero($key);
    return $cipher;
}

/**
 * Decrypt a message
 * 
 * @param string $encrypted - message encrypted with safeEncrypt()
 * @param string $key - encryption key
 * @return string
 * @throws Exception
 */
function safeDecrypt(string $encrypted, string $key): string
{   
    $decoded = base64_decode($encrypted);
    $nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
    $ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');

    $plain = sodium_crypto_secretbox_open(
        $ciphertext,
        $nonce,
        $key
    );
    if (!is_string($plain)) {
        throw new Exception('Invalid MAC');
    }
    sodium_memzero($ciphertext);
    sodium_memzero($key);
    return $plain;
}

Então para testá - lo fora:

<?php
// This refers to the previous code block.
require "safeCrypto.php"; 

// Do this once then store it somehow:
$key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
$message = 'We are all living in a yellow submarine';

$ciphertext = safeEncrypt($message, $key);
$plaintext = safeDecrypt($ciphertext, $key);

var_dump($ciphertext);
var_dump($plaintext);

Halite-Libsódio Tornou Mais Fácil

Um dos projectos em que tenho estado a trabalhar é uma biblioteca de encriptação chamada Halite, que visa tornar o libsodium mais fácil e mais intuitivo.

<?php
use \ParagonIE\Halite\KeyFactory;
use \ParagonIE\Halite\Symmetric\Crypto as SymmetricCrypto;

// Generate a new random symmetric-key encryption key. You're going to want to store this:
$key = new KeyFactory::generateEncryptionKey();
// To save your encryption key:
KeyFactory::save($key, '/path/to/secret.key');
// To load it again:
$loadedkey = KeyFactory::loadEncryptionKey('/path/to/secret.key');

$message = 'We are all living in a yellow submarine';
$ciphertext = SymmetricCrypto::encrypt($message, $key);
$plaintext = SymmetricCrypto::decrypt($ciphertext, $key);

var_dump($ciphertext);
var_dump($plaintext);

Toda a criptografia subjacente é tratada pelo libsodium.

Exemplo com desarmar/php-encriptação

<?php
/**
 * This requires https://github.com/defuse/php-encryption
 * php composer.phar require defuse/php-encryption
 */

use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;

require "vendor/autoload.php";

// Do this once then store it somehow:
$key = Key::createNewRandomKey();

$message = 'We are all living in a yellow submarine';

$ciphertext = Crypto::encrypt($message, $key);
$plaintext = Crypto::decrypt($ciphertext, $key);

var_dump($ciphertext);
var_dump($plaintext);

Nota: Crypto::encrypt() devolve a saída codificada pelo hexadecimal.

Gestão Da Chave De Encriptação

Se estás tentado a usa uma "password", pára já. Você precisa de uma chave de encriptação de 128 bits aleatória, não de uma senha humana memorável.

Você pode armazenar uma chave de encriptação para uso a longo prazo assim:

$storeMe = bin2hex($key);
E, a pedido, podes recuperá-lo assim.
$key = hex2bin($storeMe);

I fortemente recomendo apenas armazenar uma chave gerada aleatoriamente para uso a longo prazo em vez de qualquer tipo de senha como a chave (ou para derivar a chave).

Se estás a usar o desarme biblioteca:

" mas eu realmente quero usar uma senha."

Isso é uma má ideia, mas está bem, aqui está como fazê-lo em segurança. Primeiro, gera uma chave aleatória e guarda-a numa constante.
/**
 * Replace this with your own salt! 
 * Use bin2hex() then add \x before every 2 hex characters, like so:
 */
define('MY_PBKDF2_SALT', "\x2d\xb7\x68\x1a\x28\x15\xbe\x06\x33\xa0\x7e\x0e\x8f\x79\xd5\xdf");
Repare que está a adicionar trabalho extra e pode usar esta constante como chave e poupar-se a muitas dores de cabeça!

Utilize então o PBKDF2 (como este) para obter um chave de encriptação da sua senha, em vez de cifrar directamente com a sua senha.

/**
 * Get an AES key from a static password and a secret salt
 * 
 * @param string $password Your weak password here
 * @param int $keysize Number of bytes in encryption key
 */
function getKeyFromPassword($password, $keysize = 16)
{
    return hash_pbkdf2(
        'sha256',
        $password,
        MY_PBKDF2_SALT,
        100000, // Number of iterations
        $keysize,
        true
    );
}
Não uses apenas uma senha de 16 caracteres. A tua chave de encriptação será quebrada comicamente.
 334
Author: Scott Arciszewski, 2018-07-24 12:11:36

O que não fazer

Aviso:
Esta resposta utiliza O BCE . O BCE não é um modo de encriptação, é apenas um bloco de construção. A utilização do BCE, como demonstrado na presente resposta, não encripta efectivamente a cadeia de caracteres de forma segura. Não utilize o BCE no seu código. Veja a resposta de Scott para uma boa solução.

Tenho-o comigo mesmo. Na verdade, encontrei uma resposta no google e modifiquei uma coisa. o resultado é completamente inseguro entanto.
<?php
define("ENCRYPTION_KEY", "!@#$%^&*");
$string = "This is the original data string!";

echo $encrypted = encrypt($string, ENCRYPTION_KEY);
echo "<br />";
echo $decrypted = decrypt($encrypted, ENCRYPTION_KEY);

/**
 * Returns an encrypted & utf8-encoded
 */
function encrypt($pure_string, $encryption_key) {
    $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    $encrypted_string = mcrypt_encrypt(MCRYPT_BLOWFISH, $encryption_key, utf8_encode($pure_string), MCRYPT_MODE_ECB, $iv);
    return $encrypted_string;
}

/**
 * Returns decrypted original string
 */
function decrypt($encrypted_string, $encryption_key) {
    $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    $decrypted_string = mcrypt_decrypt(MCRYPT_BLOWFISH, $encryption_key, $encrypted_string, MCRYPT_MODE_ECB, $iv);
    return $decrypted_string;
}
?>
 42
Author: 夏期劇場, 2015-05-11 22:12:32

Estou atrasado para a festa, mas à procura da forma correcta de o fazer, deparei-me com esta página, que foi um dos melhores resultados do Google search, por isso gostaria de partilhar a minha opinião sobre o problema, que considero estar actualizado no momento em que redigimos este post (início de 2017). Do PHP 7. 1. 0, o mcrypt_decrypt e o mcrypt_encrypt vão ser desactualizados, por isso a construção do código de prova futuro deverá usar o openssl_ encrypt e o openssl_ decrypt

Podes fazer alguma coisa. tipo:
$string_to_encrypt="Test";
$password="password";
$encrypted_string=openssl_encrypt($string_to_encrypt,"AES-128-ECB",$password);
$decrypted_string=openssl_decrypt($encrypted_string,"AES-128-ECB",$password);

Importante : isto usa O modo BCE, que não é seguro. Se você quiser uma solução simples sem fazer um curso intensivo de engenharia criptográfica, não a Escreva você mesmo, basta usar uma biblioteca .

Você pode usar qualquer outro método chipper também, dependendo da sua necessidade de segurança. Para descobrir os métodos de chipper disponíveis, veja por favor a função openssl_get_cipher_metods.

 28
Author: Emil Borconi, 2017-04-11 20:42:57

Para o quadro Laravel

Se está a usar a plataforma Laravel, então é mais fácil cifrar e descodificar com funções internas.

$string = 'Some text to be encrypted';
$encrypted = \Illuminate\Support\Facades\Crypt::encrypt($string);
$decrypted_string = \Illuminate\Support\Facades\Crypt::decrypt($encrypted);

var_dump($string);
var_dump($encrypted);
var_dump($decrypted_string);

Nota: não se esqueça de definir uma cadeia aleatória de 16, 24 ou 32 caracteres na opção Chave da configuração/aplicação.ficheiro php. Caso contrário, valores cifrados não será seguro.

 11
Author: Somnath Muluk, 2015-12-14 12:58:54

Nota histórica: {[5] } isto foi escrito na época do PHP4. Isto é o que chamamos de "código legado" agora.

Deixei esta resposta para fins históricos - mas alguns dos métodos estão agora desactualizados, o método de encriptação DES não é uma prática recomendada, etc.

Eu não atualizei este código por duas razões: 1) eu já não trabalho com métodos de criptografia à mão em PHP, e 2) este código ainda serve o propósito que se destinava a: conceito mínimo e simplista de como a criptografia pode funcionar em PHP.

Se você encontrar uma fonte igualmente simplista, "PHP encryption for dummies" tipo de fonte que pode fazer com que as pessoas comecem em 10-20 linhas de código ou menos, deixe-me saber em comentários.

Para além disso, por favor, desfrutem deste episódio clássico de encriptação minimalista PHP4.

Idealmente você tem - ou pode ter-acesso à biblioteca mcrypt PHP, uma vez que é certamente popular e muito útil uma variedade de Tarefas. Aqui está um resumo dos diferentes tipos de criptografia e algum código de exemplo: técnicas de criptografia em PHP

//Listing 3: Encrypting Data Using the mcrypt_ecb Function 

<?php 
echo("<h3> Symmetric Encryption </h3>"); 
$key_value = "KEYVALUE"; 
$plain_text = "PLAINTEXT"; 
$encrypted_text = mcrypt_ecb(MCRYPT_DES, $key_value, $plain_text, MCRYPT_ENCRYPT); 
echo ("<p><b> Text after encryption : </b>"); 
echo ( $encrypted_text ); 
$decrypted_text = mcrypt_ecb(MCRYPT_DES, $key_value, $encrypted_text, MCRYPT_DECRYPT); 
echo ("<p><b> Text after decryption : </b>"); 
echo ( $decrypted_text ); 
?> 

Alguns avisos:

1) Nunca utilize encriptação reversível ou" simétrica " quando uma faixa de um só sentido serve.

2) Se os dados são verdadeiramente sensíveis, como os números do cartão de crédito ou da Segurança Social, pare; você precisa de mais do que qualquer simples pedaço de código irá fornecer, mas ao invés disso você precisa de uma biblioteca criptográfica projetada para este fim e uma quantidade significativa de tempo para pesquisar os métodos necessários. Além disso, a criptografia do software é provavelmente

3) qualquer tipo de encriptação facilmente implementável, conforme listado aqui, pode razoavelmente proteger informações ligeiramente importantes que você quer manter de olhos curiosos ou de exposição limite em caso de fugas acidentais/intencionais. Mas vendo como a chave é armazenada em texto simples no servidor web, se eles podem obter os dados eles podem obter a chave de decriptação.

Seja como for, Diverte-te.
 6
Author: BrianH, 2015-05-11 22:12:01

Se não quiser usar a biblioteca (o que deve fazer), então use algo assim (PHP 7):

function sign($message, $key) {
    return hash_hmac('sha256', $message, $key) . $message;
}

function verify($bundle, $key) {
    return hash_equals(
      hash_hmac('sha256', mb_substr($bundle, 64, null, '8bit'), $key),
      mb_substr($bundle, 0, 64, '8bit')
    );
}

function getKey($password, $keysize = 16) {
    return hash_pbkdf2('sha256',$password,'some_token',100000,$keysize,true);
}

function encrypt($message, $password) {
    $iv = random_bytes(16);
    $key = getKey($password);
    $result = sign(openssl_encrypt($message,'aes-256-ctr',$key,OPENSSL_RAW_DATA,$iv), $key);
    return bin2hex($iv).bin2hex($result);
}

function decrypt($hash, $password) {
    $iv = hex2bin(substr($hash, 0, 32));
    $data = hex2bin(substr($hash, 32));
    $key = getKey($password);
    if (!verify($data, $key)) {
      return null;
    }
    return openssl_decrypt(mb_substr($data, 64, null, '8bit'),'aes-256-ctr',$key,OPENSSL_RAW_DATA,$iv);
}

$string_to_encrypt='John Smith';
$password='password';
$encrypted_string=encrypt($string_to_encrypt, $password);
$decrypted_string=decrypt($encrypted_string, $password);
 1
Author: Ascon, 2018-05-16 15:27:21