Önsöz
Tablo tanımınızdan başlayarak:
- UserID
- Fname
- Lname
- Email
- Password
- IV
İşte değişiklikler:
- Alanları
Fname
, Lname
ve Email
sağladığı bir simetrik şifreleme, ile şifrelenir OpenSSL'de ,
IV
Alan saklayacak başlatma vektörü şifreleme için kullandı. Depolama gereksinimleri, kullanılan şifre ve moda bağlıdır; bunun hakkında daha sonra daha fazla bilgi.
Password
Alan bir kullanılarak karma hale edilecektir tek yönlü , şifre karma
Şifreleme
Şifreleme ve mod
En iyi şifreleme şifresini ve modunu seçmek bu cevabın kapsamı dışındadır, ancak son seçim hem şifreleme anahtarının hem de başlatma vektörünün boyutunu etkiler; Bu yazı için, 16 baytlık sabit blok boyutuna ve 16, 24 veya 32 bayt anahtar boyutuna sahip AES-256-CBC kullanacağız.
Şifreleme anahtarı
İyi bir şifreleme anahtarı, güvenilir bir rastgele sayı oluşturucudan oluşturulan ikili bir blob'dur. Aşağıdaki örnek önerilir (> = 5.3):
$key_size = 32; // 256 bits
$encryption_key = openssl_random_pseudo_bytes($key_size, $strong);
// $strong will be true if the key is crypto safe
Bu, bir veya birden çok kez yapılabilir (bir şifreleme anahtarları zinciri oluşturmak istiyorsanız). Bunları olabildiğince gizli tutun.
IV
Başlatma vektörü, şifrelemeye rastgelelik ekler ve CBC modu için gereklidir. Bu değerler ideal olarak yalnızca bir kez kullanılmalıdır (teknik olarak şifreleme anahtarı başına bir kez), bu nedenle bir satırın herhangi bir bölümünde yapılan bir güncelleme onu yeniden oluşturmalıdır.
IV'ü oluşturmanıza yardımcı olmak için bir işlev sağlanmıştır:
$iv_size = 16; // 128 bits
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
Misal
Daha öncekini kullanarak ad alanını şifreleyelim $encryption_key
ve $iv
; bunu yapmak için verilerimizi blok boyutuna doldurmalıyız:
function pkcs7_pad($data, $size)
{
$length = $size - strlen($data) % $size;
return $data . str_repeat(chr($length), $length);
}
$name = 'Jack';
$enc_name = openssl_encrypt(
pkcs7_pad($name, 16), // padded data
'AES-256-CBC', // cipher and mode
$encryption_key, // secret key
0, // options (not used)
$iv // initialisation vector
);
Depolama gereksinimleri
Şifrelenmiş çıktı, tıpkı IV gibi, ikilidir; bu değerleri bir veritabanında depolamak, BINARY
veya gibi belirlenmiş sütun türleri kullanılarak gerçekleştirilebilir VARBINARY
.
IV gibi çıkış değeri ikilidir; bu değerleri MySQL'de saklamak için BINARY
veyaVARBINARY
sütunlarını kullanmayı düşünün . Bu bir seçenek değilse, ikili verileri base64_encode()
veya kullanarak metinsel gösterime de dönüştürebilirsiniz bin2hex()
, bunu yapmak% 33 ila% 100 daha fazla depolama alanı gerektirir.
Şifre çözme
Depolanan değerlerin şifresinin çözülmesi benzerdir:
function pkcs7_unpad($data)
{
return substr($data, 0, -ord($data[strlen($data) - 1]));
}
$row = $result->fetch(PDO::FETCH_ASSOC); // read from database result
// $enc_name = base64_decode($row['Name']);
// $enc_name = hex2bin($row['Name']);
$enc_name = $row['Name'];
// $iv = base64_decode($row['IV']);
// $iv = hex2bin($row['IV']);
$iv = $row['IV'];
$name = pkcs7_unpad(openssl_decrypt(
$enc_name,
'AES-256-CBC',
$encryption_key,
0,
$iv
));
Kimliği doğrulanmış şifreleme
Gizli bir anahtardan (şifreleme anahtarından farklı) ve şifre metninden oluşturulan bir imza ekleyerek, oluşturulan şifreleme metninin bütünlüğünü daha da iyileştirebilirsiniz. Şifreleme metninin şifresi çözülmeden önce, imza ilk olarak doğrulanır (tercihen sabit zamanlı bir karşılaştırma yöntemiyle).
Misal
// generate once, keep safe
$auth_key = openssl_random_pseudo_bytes(32, $strong);
// authentication
$auth = hash_hmac('sha256', $enc_name, $auth_key, true);
$auth_enc_name = $auth . $enc_name;
// verification
$auth = substr($auth_enc_name, 0, 32);
$enc_name = substr($auth_enc_name, 32);
$actual_auth = hash_hmac('sha256', $enc_name, $auth_key, true);
if (hash_equals($auth, $actual_auth)) {
// perform decryption
}
Ayrıca bakınız: hash_equals()
Karma
Veritabanınızda tersine çevrilebilir bir şifre saklamaktan mümkün olduğunca kaçınılmalıdır; içeriğini bilmek yerine yalnızca parolayı doğrulamak istersiniz. Bir kullanıcı şifresini kaybederse, orijinal şifresini göndermek yerine şifresini sıfırlamasına izin vermek daha iyidir (şifre sıfırlamanın yalnızca sınırlı bir süre için yapılabileceğinden emin olun).
Bir karma işlevi uygulamak tek yönlü bir işlemdir; daha sonra orijinal veriler ifşa edilmeden doğrulama için güvenle kullanılabilir; Parolalar için kaba kuvvet yöntemi, nispeten kısa uzunluğu ve birçok kişinin zayıf parola seçimleri nedeniyle onu ortaya çıkarmak için uygun bir yaklaşımdır.
Dosya içeriklerini bilinen bir karma değerle karşılaştırmak için MD5 veya SHA1 gibi karma algoritmalar yapılmıştır. Bu doğrulamayı olabildiğince hızlı yapmak için büyük ölçüde optimize edilmişlerdir ve yine de doğru olurlar. Nispeten sınırlı çıktı alanları göz önüne alındığında, bilinen parolalar ve ilgili hash çıktıları olan gökkuşağı tablolarıyla bir veritabanı oluşturmak kolaydı.
Parolaya hashing uygulamadan önce bir tuz eklemek gökkuşağı tablosunu işe yaramaz hale getirebilirdi, ancak son donanım gelişmeleri kaba kuvvet aramalarını uygulanabilir bir yaklaşım haline getirdi. Bu nedenle, kasıtlı olarak yavaş ve optimize edilmesi imkansız bir karma algoritmaya ihtiyacınız var. Ayrıca, daha hızlı donanım için yükü, gelecekteki parola karmalarını doğrulama becerisini etkilemeden artırabilmelidir.
Şu anda iki popüler seçenek bulunmaktadır:
- PBKDF2 (Parola Tabanlı Anahtar Türetme İşlevi v2)
- bcrypt (Blowfish olarak da bilinir)
Bu cevap bcrypt ile bir örnek kullanacaktır.
nesil
Aşağıdaki gibi bir şifre karması oluşturulabilir:
$password = 'my password';
$random = openssl_random_pseudo_bytes(18);
$salt = sprintf('$2y$%02d$%s',
13, // 2^n cost factor
substr(strtr(base64_encode($random), '+', '.'), 0, 22)
);
$hash = crypt($password, $salt);
Tuz, openssl_random_pseudo_bytes()
rastgele bir veri bloğu oluşturmak için üretilir ve bu daha sonra içinden geçer base64_encode()
ve strtr()
gerekli alfabesiyle eşleşir [A-Za-z0-9/.]
.
crypt()
Karma (algoritmasına göre işlev yapar $2y$
Blowfish'ten için), maliyet faktörü ve 22 karakter tuzu (13 faktör 3 GHz makinesinde yaklaşık 0.40s alır).
onaylama
Kullanıcı bilgilerini içeren satırı getirdikten sonra, parolayı şu şekilde doğrularsınız:
$given_password = $_POST['password']; // the submitted password
$db_hash = $row['Password']; // field with the password hash
$given_hash = crypt($given_password, $db_hash);
if (isEqual($given_hash, $db_hash)) {
// user password verified
}
// constant time string compare
function isEqual($str1, $str2)
{
$n1 = strlen($str1);
if (strlen($str2) != $n1) {
return false;
}
for ($i = 0, $diff = 0; $i != $n1; ++$i) {
$diff |= ord($str1[$i]) ^ ord($str2[$i]);
}
return !$diff;
}
Bir şifreyi doğrulamak için crypt()
tekrar arayın, ancak tuz değeri olarak önceden hesaplanan karma değeri iletirsiniz. Verilen şifre karma ile eşleşirse, dönüş değeri aynı karmayı verir. Karmayı doğrulamak için, zamanlama saldırılarını önlemek için genellikle sabit zamanlı bir karşılaştırma işlevi kullanmanız önerilir.
PHP 5.5 ile parola hashingi
PHP 5.5 , yukarıdaki karma yöntemini basitleştirmek için kullanabileceğiniz parola karma işlevlerini tanıttı :
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 13]);
Ve doğrulanıyor:
if (password_verify($given_password, $db_hash)) {
// password valid
}
Ayrıca bakınız: password_hash()
,password_verify()