Important : A moins que vous n'ayez un très un cas d'utilisation particulier, ne pas crypter les mots de passe utilisez plutôt un algorithme de hachage de mot de passe. Lorsque quelqu'un dit qu'il crypter leurs mots de passe dans une application côté serveur, soit ils sont mal informés, soit ils décrivent une conception de système dangereuse. Stocker les mots de passe en toute sécurité est un problème totalement distinct du cryptage.
Soyez informé. Concevez des systèmes sûrs.
Cryptage de données portable en PHP
Si vous utilisez PHP 5.4 ou plus récent et que vous ne voulez pas écrire vous-même un module de cryptographie, je vous recommande d'utiliser une bibliothèque existante qui fournit un cryptage authentifié . La bibliothèque que j'ai liée ne s'appuie que sur ce que PHP fournit et fait l'objet d'une révision périodique par une poignée de chercheurs en sécurité. (Moi-même inclus.)
Si vos objectifs de portabilité n'empêchent pas de demander des extensions PECL, libsodium es hautement recommandé par rapport à tout ce que vous ou moi pouvons écrire en PHP.
Mise à jour (2016-06-12) : Vous pouvez maintenant utiliser sodium_compat et utiliser la même crypto libsodium offre sans installer les extensions PECL.
Si vous voulez vous essayer à l'ingénierie de la cryptographie, lisez la suite.
Tout d'abord, vous devez prendre le temps d'apprendre les dangers du cryptage non authentifié y le principe du Doom cryptographique .
- Les données cryptées peuvent toujours être altérées par un utilisateur malveillant.
- L'authentification des données cryptées empêche la falsification.
- L'authentification des données non cryptées n'empêche pas la falsification.
Cryptage et décryptage
Le cryptage en PHP est en fait simple (nous allons utiliser la fonction openssl_encrypt()
y openssl_decrypt()
une fois que vous aurez pris des décisions sur la manière de crypter vos informations. Consultez openssl_get_cipher_methods()
pour obtenir la liste des méthodes prises en charge par votre système. Le meilleur choix est AES en mode CTR :
aes-128-ctr
aes-192-ctr
aes-256-ctr
Il n'y a actuellement aucune raison de croire que le Taille de la clé AES est un problème important dont il faut s'inquiéter (plus important est probablement pas mieux, en raison d'un mauvais ordonnancement des clés dans le mode 256 bits).
Nota: Nous n'utilisons pas mcrypt
parce que c'est abandonware et a bogues non corrigés qui pourraient affecter la sécurité. Pour ces raisons, j'encourage les autres développeurs PHP à l'éviter également.
Wrapper de chiffrement/déchiffrement simple utilisant OpenSSL
class UnsafeCrypto
{
const METHOD = 'aes-256-ctr';
/**
* Encrypts (but does not authenticate) a message
*
* @param string $message - plaintext message
* @param string $key - encryption key (raw binary expected)
* @param boolean $encode - set to TRUE to return a base64-encoded
* @return string (raw binary)
*/
public static function encrypt($message, $key, $encode = false)
{
$nonceSize = openssl_cipher_iv_length(self::METHOD);
$nonce = openssl_random_pseudo_bytes($nonceSize);
$ciphertext = openssl_encrypt(
$message,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$nonce
);
// Now let's pack the IV and the ciphertext together
// Naively, we can just concatenate
if ($encode) {
return base64_encode($nonce.$ciphertext);
}
return $nonce.$ciphertext;
}
/**
* Decrypts (but does not verify) a message
*
* @param string $message - ciphertext message
* @param string $key - encryption key (raw binary expected)
* @param boolean $encoded - are we expecting an encoded string?
* @return string
*/
public static function decrypt($message, $key, $encoded = false)
{
if ($encoded) {
$message = base64_decode($message, true);
if ($message === false) {
throw new Exception('Encryption failure');
}
}
$nonceSize = openssl_cipher_iv_length(self::METHOD);
$nonce = mb_substr($message, 0, $nonceSize, '8bit');
$ciphertext = mb_substr($message, $nonceSize, null, '8bit');
$plaintext = openssl_decrypt(
$ciphertext,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$nonce
);
return $plaintext;
}
}
Exemple d'utilisation
$message = 'Ready your ammunition; we attack at dawn.';
$key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');
$encrypted = UnsafeCrypto::encrypt($message, $key);
$decrypted = UnsafeCrypto::decrypt($encrypted, $key);
var_dump($encrypted, $decrypted);
Démo : https://3v4l.org/jl7qR
La bibliothèque cryptographique simple ci-dessus n'est toujours pas sûre à utiliser. Nous devons authentifier les cryptogrammes et les vérifier avant de les décrypter. .
Note : Par défaut, UnsafeCrypto::encrypt()
retournera une chaîne binaire brute. Appelez-la de cette façon si vous avez besoin de la stocker dans un format sûr pour les binaires (encodé en base64) :
$message = 'Ready your ammunition; we attack at dawn.';
$key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');
$encrypted = UnsafeCrypto::encrypt($message, $key, true);
$decrypted = UnsafeCrypto::decrypt($encrypted, $key, true);
var_dump($encrypted, $decrypted);
Démo : http://3v4l.org/f5K93
Wrapper d'authentification simple
class SaferCrypto extends UnsafeCrypto
{
const HASH_ALGO = 'sha256';
/**
* Encrypts then MACs a message
*
* @param string $message - plaintext message
* @param string $key - encryption key (raw binary expected)
* @param boolean $encode - set to TRUE to return a base64-encoded string
* @return string (raw binary)
*/
public static function encrypt($message, $key, $encode = false)
{
list($encKey, $authKey) = self::splitKeys($key);
// Pass to UnsafeCrypto::encrypt
$ciphertext = parent::encrypt($message, $encKey);
// Calculate a MAC of the IV and ciphertext
$mac = hash_hmac(self::HASH_ALGO, $ciphertext, $authKey, true);
if ($encode) {
return base64_encode($mac.$ciphertext);
}
// Prepend MAC to the ciphertext and return to caller
return $mac.$ciphertext;
}
/**
* Decrypts a message (after verifying integrity)
*
* @param string $message - ciphertext message
* @param string $key - encryption key (raw binary expected)
* @param boolean $encoded - are we expecting an encoded string?
* @return string (raw binary)
*/
public static function decrypt($message, $key, $encoded = false)
{
list($encKey, $authKey) = self::splitKeys($key);
if ($encoded) {
$message = base64_decode($message, true);
if ($message === false) {
throw new Exception('Encryption failure');
}
}
// Hash Size -- in case HASH_ALGO is changed
$hs = mb_strlen(hash(self::HASH_ALGO, '', true), '8bit');
$mac = mb_substr($message, 0, $hs, '8bit');
$ciphertext = mb_substr($message, $hs, null, '8bit');
$calculated = hash_hmac(
self::HASH_ALGO,
$ciphertext,
$authKey,
true
);
if (!self::hashEquals($mac, $calculated)) {
throw new Exception('Encryption failure');
}
// Pass to UnsafeCrypto::decrypt
$plaintext = parent::decrypt($ciphertext, $encKey);
return $plaintext;
}
/**
* Splits a key into two separate keys; one for encryption
* and the other for authenticaiton
*
* @param string $masterKey (raw binary)
* @return array (two raw binary strings)
*/
protected static function splitKeys($masterKey)
{
// You really want to implement HKDF here instead!
return [
hash_hmac(self::HASH_ALGO, 'ENCRYPTION', $masterKey, true),
hash_hmac(self::HASH_ALGO, 'AUTHENTICATION', $masterKey, true)
];
}
/**
* Compare two strings without leaking timing information
*
* @param string $a
* @param string $b
* @ref https://paragonie.com/b/WS1DLx6BnpsdaVQW
* @return boolean
*/
protected static function hashEquals($a, $b)
{
if (function_exists('hash_equals')) {
return hash_equals($a, $b);
}
$nonce = openssl_random_pseudo_bytes(32);
return hash_hmac(self::HASH_ALGO, $a, $nonce) === hash_hmac(self::HASH_ALGO, $b, $nonce);
}
}
Exemple d'utilisation
$message = 'Ready your ammunition; we attack at dawn.';
$key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');
$encrypted = SaferCrypto::encrypt($message, $key);
$decrypted = SaferCrypto::decrypt($encrypted, $key);
var_dump($encrypted, $decrypted);
Démos : binaire brut , base64-encodé
Si quelqu'un souhaite utiliser cette SaferCrypto
dans un environnement de production, ou votre propre mise en œuvre des mêmes concepts, je vous recommande vivement de vous adresser à vos cryptographes résidents pour un second avis avant de le faire. Ils pourront vous parler d'erreurs dont je ne suis peut-être même pas au courant.
Vous ferez mieux d'utiliser une bibliothèque de cryptographie réputée .
7 votes
Il suffit de faire un XOR de votre chaîne.
3 votes
Pour un chiffrement à usage général, utilisez defuse/php-encryption/ au lieu d'en faire un.
3 votes
Mains éloignées de github.com/defuse/php-encryption - il est plus lent de plusieurs ordres de grandeur que Mcrypt.
0 votes
defuse/php-encryption
utiliseopenssl()
qui peut tirer partiAES-NI
(ce qui n'est pas le cas de libmcrypt) et, selon la quantité de données que vous chiffrez, peut donc fournir un débit de chiffrement beaucoup plus rapide que mcrypt. Veuillez poster un benchmark côte à côte pour justifier votre affirmation.0 votes
De plus, @EugenRieck, le cryptage ne sera probablement pas le goulot d'étranglement de votre application. Si c'est le cas, utilisez libsodium ;)
1 votes
@Scott C'est en pensant que "ce ne sera probablement pas le goulot d'étranglement" que nous avons eu beaucoup de mauvais logiciels.
5 votes
Si vous cryptez/décryptez beaucoup de données au point que les millisecondes que cela vous coûte ralentissent votre application, prenez votre mal en patience et passez à libsodium.
Sodium::crypto_secretbox()
ySodium::crypto_secretbox_open()
sont sécurisés et performants.0 votes
Depuis que mon commentaire précédent a été posté, les liens PHP de libsodium ont changé. Maintenant, vous devez utiliser
\Sodium\crypto_secretbox()
y\Sodium\crypto_secretbox_open()
.0 votes
Je vous suggère d'utiliser la nouvelle bibliothèque Sodium en PHP 7.1+. Cette bibliothèque a seulement besoin d'une clé secrète et d'une clé publique comme paramètre, et pour chaque ligne d'un nonce unique. Cette bibliothèque (wrapper) peut vous aider, les méthodes sont faciles à utiliser et vous n'avez pas besoin de comprendre la bibliothèque Sodium vous-même : github.com/internetpixels/sodium-encryption
0 votes
Rot13 avec un code de passe nul, imho
0 votes
Oui, c'est ce que je recherche.