Başka bir şey yapmadan önce, şifreleme ve kimlik doğrulama arasındaki farkı ve neden sadece şifreleme yerine kimlik doğrulamalı şifreleme istediğinizi anlamaya çalışın .
Kimliği doğrulanmış şifreleme uygulamak için Önce Şifreleme sonra MAC. Şifreleme ve kimlik doğrulama sırası çok önemlidir! Bu sorunun mevcut cevaplarından biri bu hatayı yaptı; PHP'de yazılmış birçok şifreleme kütüphanesi gibi.
Sen gerektiğini Kendi şifrelemesini uygulamak önlemek ve bunun yerine güvenli bir kütüphane tarafından yazılan ve şifreleme uzmanları tarafından gözden kullanın.
Güncelleme: PHP 7.2 şimdi libsodium sağlıyor ! En iyi güvenlik için sistemlerinizi PHP 7.2 veya üstünü kullanacak şekilde güncelleyin ve yalnızca bu cevaptaki libsodium tavsiyelerine uyun.
PECL erişiminiz varsa libsodium kullanın (veya PECL'siz libsodium istiyorsanız sodyum_compat); aksi takdirde...
defuse / php-şifreleme kullanın ; kendi kriptografinizi atmayın!
Yukarıda bağlantılı her iki kütüphane de kendi kütüphanelerinize doğrulanmış şifreleme uygulamasını kolaylaştırır ve ağrısız hale getirir.
İnternetteki her şifreleme uzmanının geleneksel bilgeliğine karşı kendi şifreleme kitaplığınızı hala yazmak ve dağıtmak istiyorsanız, yapmanız gereken adımlar bunlar.
Şifreleme:
- TO modunda AES kullanarak şifreleyin. GCM'yi de kullanabilirsiniz (ayrı bir MAC ihtiyacını ortadan kaldırır). Ayrıca, ChaCha20 ve Salsa20 (tarafından sağlanan sağlanır ) akış şifreleridir ve özel modlara ihtiyaç duymazlar.
- Yukarıdaki GCM'yi seçmedikçe, şifreleme metnini HMAC-SHA-256 ile doğrulamanız gerekir (veya akış şifreleri için Poly1305 - çoğu libsodium API'si bunu sizin için yapar). MAC, IV ve şifreli metni kapsamalıdır!
Şifre çözme:
- Poly1305 veya GCM kullanılmadıkça, şifre metninin MAC'ini yeniden hesaplayın ve kullanarak gönderilen MAC ile karşılaştırın
hash_equals()
. Başarısız olursa, iptal edin.
- Mesajın şifresini çözün.
Diğer Tasarım Konuları:
- Hiçbir şeyi sıkıştırmayın. Şifre metni sıkıştırılamaz; şifrelemeden önce düz metnin sıkıştırılması bilgi sızıntılarına neden olabilir (örneğin TLS'de CRIME ve BREACH).
- Emin kullanmak olun
mb_strlen()
ve mb_substr()
kullanmadan, '8bit'
önlemek için karakter seti modu mbstring.func_overload
sorunları.
- IV'ler bir CSPRNG kullanarak oluşturulmalıdır ; Kullanıyorsanız
mcrypt_create_iv()
, KULLANMAYINMCRYPT_RAND
!
- Bir AEAD yapısı kullanmadığınız sürece, DAİMA şifreleyin ve ardından MAC!
bin2hex()
, base64_encode()
vb. şifreleme anahtarlarınız hakkında önbellek zamanlaması yoluyla bilgi sızdırabilir. Mümkünse bunlardan kaçının.
Burada verilen tavsiyelere uysanız bile, kriptografi ile çok şey ters gidebilir. Her zaman bir kriptografi uzmanının uygulamanızı gözden geçirmesini sağlayın. Yerel üniversitenizdeki bir şifreleme öğrencisi ile kişisel arkadaş olacak kadar şanslı değilseniz, her zaman Şifreleme Yığın Değişimi'ni deneyebilirsiniz tavsiye için forumunu .
Uygulamanızın profesyonel bir analizine ihtiyacınız varsa , PHP şifreleme kodunuzu gözden geçirmek için her zaman saygın bir güvenlik danışmanları ekibi kiralayabilirsiniz. (açıklama: işverenim).
Önemli: Şifreleme Ne Zaman Kullanılmamalıdır
Yapma şifrelemek şifreleri . Bununyerine, şu parola karma algoritmalarından birini kullanarak karma yapmak istiyorsunuz:
Parola depolamak için asla genel amaçlı bir karma işlevi (MD5, SHA256) kullanmayın.
URL Parametrelerini şifrelemeyin . Bu iş için yanlış araç.
Libsodium ile PHP String Şifreleme Örneği
PHP <7.2 kullanıyorsanız veya başka bir şekilde libsodium kurulu değilse , aynı sonucu elde etmek için sodyum_compat komutunu kullanabilirsiniz (daha yavaş da olsa).
<?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;
}
Sonra test etmek için:
<?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 - Libsodium Daha Kolay
Ben üzerinde çalıştığım projelerden biri olarak adlandırılan bir şifreleme kütüphanesidir Halit kolay ve daha sezgisel libsodium yapmak amaçları.
<?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);
Temeldeki kriptografinin tamamı libsodyum tarafından ele alınır.
Defuse / php-şifreleme örneği
<?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);
Not : Crypto::encrypt()
onaltılık kodlamalı çıktıyı döndürür.
Şifreleme Anahtarı Yönetimi
Eğer bir "şifre" kullanmak istiyorsanız, hemen durun. Unutulmaz 128-bit şifreleme anahtarına ihtiyacınız var, unutulmaz bir şifre değil.
Bir şifreleme anahtarını aşağıdaki gibi uzun süreli kullanım için saklayabilirsiniz:
$storeMe = bin2hex($key);
Ve talep üzerine, şu şekilde alabilirsiniz:
$key = hex2bin($storeMe);
Ben kuvvetle sadece uzun süreli kullanım için rastgele oluşturulmuş anahtarı yerine anahtar olarak şifre her türlü depolamak önerilir (veya anahtarı türetmek).
Defuse kütüphanesini kullanıyorsanız:
"Ama ben gerçekten bir şifre kullanmak istiyorum."
Bu kötü bir fikir, ama tamam, bunu nasıl güvenli bir şekilde yapacağınız.
İlk olarak, rastgele bir anahtar oluşturun ve sabit bir yerde saklayın.
/**
* 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");
Ekstra iş eklediğinizi ve bu sabiti anahtar olarak kullanabileceğinizi ve kendinizi çok fazla acı çekebileceğini unutmayın!
Ardından, doğrudan şifrenizle şifrelemek yerine şifrenizden uygun bir şifreleme anahtarı elde etmek için PBKDF2'yi (böyle) kullanın.
/**
* 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
);
}
Sadece 16 karakterli bir şifre kullanmayın. Şifreleme anahtarınız komik bir şekilde bozulacaktır.