Jak sugeruje @rqLizard , możesz zamiast tego użyć funkcji openssl_encrypt
/ openssl_decrypt
PHP, które zapewniają znacznie lepszą alternatywę dla implementacji AES (The Advanced Encryption Standard), znanego również jako szyfrowanie Rijndael.
Zgodnie z poniższym komentarzem Scotta na php.net :
Jeśli piszesz kod do szyfrowania / szyfrowania danych w 2015 roku, powinieneś użyć openssl_encrypt()
i openssl_decrypt()
. Podstawowa biblioteka ( libmcrypt
) została porzucona od 2007 roku i działa znacznie gorzej niż OpenSSL (który wykorzystuje AES-NI
nowoczesne procesory i jest bezpieczny w czasie buforowania).
Nie MCRYPT_RIJNDAEL_256
jest też AES-256
, to inny wariant szyfru blokowego Rijndael. Jeśli chcesz AES-256
w mcrypt
, trzeba korzystać MCRYPT_RIJNDAEL_128
z kluczem 32-bajtowy. OpenSSL sprawia, że jest bardziej oczywiste, którego trybu używasz (tj. aes-128-cbc
Vs aes-256-ctr
).
OpenSSL używa również wypełnienia PKCS7 w trybie CBC zamiast wypełniania bajtów NULL w mcrypt. W związku z tym mcrypt jest bardziej podatny na ataki typu padding oracle niż OpenSSL.
Wreszcie, jeśli nie uwierzytelniasz swoich zaszyfrowanych tekstów (Szyfruj następnie MAC), robisz to źle.
Dalsze czytanie:
Przykłady kodu
Przykład 1
Szyfrowanie uwierzytelnione AES w trybie GCM, przykład dla PHP 7.1+
<?php
//$key should have been previously generated in a cryptographically safe way, like openssl_random_pseudo_bytes
$plaintext = "message to be encrypted";
$cipher = "aes-128-gcm";
if (in_array($cipher, openssl_get_cipher_methods()))
{
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext = openssl_encrypt($plaintext, $cipher, $key, $options=0, $iv, $tag);
//store $cipher, $iv, and $tag for decryption later
$original_plaintext = openssl_decrypt($ciphertext, $cipher, $key, $options=0, $iv, $tag);
echo $original_plaintext."\n";
}
?>
Przykład nr 2
Przykład szyfrowania uwierzytelnionego AES dla PHP 5.6+
<?php
//$key previously generated safely, ie: openssl_random_pseudo_bytes
$plaintext = "message to be encrypted";
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
$ciphertext = base64_encode( $iv.$hmac.$ciphertext_raw );
//decrypt later....
$c = base64_decode($ciphertext);
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = substr($c, 0, $ivlen);
$hmac = substr($c, $ivlen, $sha2len=32);
$ciphertext_raw = substr($c, $ivlen+$sha2len);
$original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$calcmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
if (hash_equals($hmac, $calcmac))//PHP 5.6+ timing attack safe comparison
{
echo $original_plaintext."\n";
}
?>
Przykład nr 3
W oparciu o powyższe przykłady zmieniłem następujący kod, który ma na celu zaszyfrowanie identyfikatora sesji użytkownika:
class Session {
/**
* Encrypts the session ID and returns it as a base 64 encoded string.
*
* @param $session_id
* @return string
*/
public function encrypt($session_id) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Encrypt the session ID.
$encrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $session_id, MCRYPT_MODE_CBC, $iv);
// Base 64 encode the encrypted session ID.
$encryptedSessionId = base64_encode($encrypt);
// Return it.
return $encryptedSessionId;
}
/**
* Decrypts a base 64 encoded encrypted session ID back to its original form.
*
* @param $encryptedSessionId
* @return string
*/
public function decrypt($encryptedSessionId) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Decode the encrypted session ID from base 64.
$decoded = base64_decode($encryptedSessionId);
// Decrypt the string.
$decryptedSessionId = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $decoded, MCRYPT_MODE_CBC, $iv);
// Trim the whitespace from the end.
$session_id = rtrim($decryptedSessionId, "\0");
// Return it.
return $session_id;
}
public function _getIv() {
return md5($this->_getSalt());
}
public function _getSalt() {
return md5($this->drupal->drupalGetHashSalt());
}
}
w:
class Session {
const SESS_CIPHER = 'aes-128-cbc';
/**
* Encrypts the session ID and returns it as a base 64 encoded string.
*
* @param $session_id
* @return string
*/
public function encrypt($session_id) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Encrypt the session ID.
$ciphertext = openssl_encrypt($session_id, self::SESS_CIPHER, $key, $options=OPENSSL_RAW_DATA, $iv);
// Base 64 encode the encrypted session ID.
$encryptedSessionId = base64_encode($ciphertext);
// Return it.
return $encryptedSessionId;
}
/**
* Decrypts a base 64 encoded encrypted session ID back to its original form.
*
* @param $encryptedSessionId
* @return string
*/
public function decrypt($encryptedSessionId) {
// Get the Drupal hash salt as a key.
$key = $this->_getSalt();
// Get the iv.
$iv = $this->_getIv();
// Decode the encrypted session ID from base 64.
$decoded = base64_decode($encryptedSessionId, TRUE);
// Decrypt the string.
$decryptedSessionId = openssl_decrypt($decoded, self::SESS_CIPHER, $key, $options=OPENSSL_RAW_DATA, $iv);
// Trim the whitespace from the end.
$session_id = rtrim($decryptedSessionId, '\0');
// Return it.
return $session_id;
}
public function _getIv() {
$ivlen = openssl_cipher_iv_length(self::SESS_CIPHER);
return substr(md5($this->_getSalt()), 0, $ivlen);
}
public function _getSalt() {
return $this->drupal->drupalGetHashSalt();
}
}
Aby wyjaśnić, powyższa zmiana nie jest prawdziwą konwersją, ponieważ te dwa szyfrowania wykorzystują inny rozmiar bloku i inne zaszyfrowane dane. Ponadto domyślne wypełnienie jest inne, MCRYPT_RIJNDAEL
obsługuje tylko niestandardowe wypełnienie zerowe. @zaph
Dodatkowe uwagi (z komentarzy @ zaph):
- Rijndael 128 (
MCRYPT_RIJNDAEL_128
) jest odpowiednikiem AES , jednak Rijndael 256 ( MCRYPT_RIJNDAEL_256
) nie jest AES-256, ponieważ 256 określa rozmiar bloku 256-bitów, podczas gdy AES ma tylko jeden rozmiar bloku: 128-bitów. Zasadniczo więc Rijndael z blokiem o rozmiarze 256 bitów ( MCRYPT_RIJNDAEL_256
) został błędnie nazwany z powodu wyborów dokonanych przez programistów mcrypt . @zaph
- Rijndael z blokiem o rozmiarze 256 może być mniej bezpieczny niż z blokiem o rozmiarze 128-bitowym, ponieważ ten ostatni miał znacznie więcej recenzji i zastosowań. Po drugie, interoperacyjność jest utrudniona, ponieważ AES jest ogólnie dostępna, podczas gdy Rijndael z blokiem o rozmiarze 256 bitów nie.
Szyfrowanie z różnymi rozmiarami bloków dla Rijndael daje różne zaszyfrowane dane.
Na przykład MCRYPT_RIJNDAEL_256
(nie równoważne AES-256
) definiuje inny wariant szyfru blokowego Rijndael o rozmiarze 256 bitów i rozmiarze klucza opartym na przekazanym kluczu, gdzie aes-256-cbc
jest Rijndael o rozmiarze bloku 128-bitów i rozmiarze klucza 256-bitowe. Dlatego używają różnych rozmiarów bloków, które generują zupełnie inne zaszyfrowane dane, ponieważ mcrypt używa liczby do określenia rozmiaru bloku, gdzie OpenSSL użył liczby do określenia rozmiaru klucza (AES ma tylko jeden rozmiar bloku o długości 128 bitów). A więc w zasadzie AES to Rijndael z rozmiarem bloku 128-bitów i kluczami o rozmiarach 128, 192 i 256 bitów. Dlatego lepiej jest używać AES, który w OpenSSL nazywa się Rijndael 128.
password_hash
i nie weryfikowaćpassword_verify
?