Php'de veriler nasıl şifrelenir / şifresini çözülür?


110

Şu anda bir öğrenciyim ve PHP okuyorum, PHP'de basit bir veri şifreleme / şifre çözme yapmaya çalışıyorum. Bazı çevrimiçi araştırmalar yaptım ve bazıları oldukça kafa karıştırıcıydı (en azından benim için).

İşte yapmaya çalıştığım şey:

Bu alanlardan oluşan bir tablom var (Kullanıcı Kimliği, Fname, Lname, E-posta, Şifre)

Sahip olmak istediğim, tüm alanların şifrelenmesi ve ardından şifresinin çözülmesi ( sha256Herhangi bir şifreleme algoritması değilse, şifreleme / şifre çözme için kullanmak mümkün mü )

Öğrenmek istediğim bir diğer şey, hash(sha256)iyi bir "tuz" ile birleştirilmiş bir yolun nasıl oluşturulacağıdır . (Temelde basit bir şifreleme / şifre çözme uygulamasına sahip olmak istiyorum, hash(sha256)+salt) Efendim / Hanımefendi, cevaplarınız çok yardımcı olacak ve çok takdir edilecektir. Teşekkürler ++




9
SHA bir şifreleme değil, bir karmadır. Kilit nokta, hash'in orijinal verilere geri döndürülemeyeceğidir (zaten kolay değil). Muhtemelen mcrypt istiyorsunuz veya mevcut değilse phpseclib'i tavsiye ederim - çok fazla düşük seviyeli matematik içeren herhangi bir şeyin saf PHP uygulamasının sloooooowww olacağını unutmamak önemli olsa da ... Bu yüzden phpseclib'i seviyorum, çünkü varsa önce mcrypt kullanır ve son çare olarak yalnızca PHP uygulamalarına geri döner.
DaveRandom

7
Normalde bir parolanın şifresini çözebilmek istemezsiniz!
Ja͢ck

1
Temel olarak bu seviyede şifreleme düşünmemelisiniz, erişim kontrolü, gizlilik, bütünlük ve kimlik doğrulama hakkında düşünmelisiniz. Bundan sonra, muhtemelen şifreleme veya güvenli hashing kullanarak bunu nasıl başarabileceğinizi kontrol edin . Şifrelerin ve benzerlerinin güvenli bir şekilde karıştırılmasını anlamak için PBKDF2 ve bcrypt / scrypt'i okumak isteyebilirsiniz.
Maarten Bodewes

Yanıtlar:


289

Önsöz

Tablo tanımınızdan başlayarak:

- UserID
- Fname
- Lname
- Email
- Password
- IV

İşte değişiklikler:

  1. Alanları Fname, Lnameve Emailsağladığı bir simetrik şifreleme, ile şifrelenir OpenSSL'de ,
  2. IVAlan 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.
  3. PasswordAlan 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_keyve $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, BINARYveya 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 BINARYveyaVARBINARY 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:

  1. PBKDF2 (Parola Tabanlı Anahtar Türetme İşlevi v2)
  2. 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()


En güvenli bahis için ad, soyad, e-posta vb. Saklamak için ne kadar süre kullanmalıyım? varbinary (???)
BentCoder

2
Elbette, ama nasıl kullanıldığına bağlı. Bir şifreleme kitaplığı yayınlarsanız, geliştiricilerin onu nasıl uygulayacağını bilemezsiniz. Bu nedenle github.com/defuse/php-encryption , kimliği doğrulanmış simetrik anahtar şifreleme sağlar ve geliştiricilerin kodunu düzenlemeden onu zayıflatmasına izin vermez.
Scott Arciszewski

2
@Scott Çok iyi, kimliği doğrulanmış şifreleme örneği ekledim;
ittiğin

1
Kimliği doğrulanmış şifreleme için +1. Soruda, burada AE'nin gerekli olmadığını söylemek için yeterli bilgi yok. Kesinlikle SQL trafiği, veritabanından depolamaya giden trafik gibi, genellikle bilinmeyen güvenlik özelliklerine sahip bir ağ üzerinden gider. Yedeklemeler ve çoğaltma da. Tehdit modeli nedir? Soru söylemiyor ve varsayımlarda bulunmak tehlikeli olabilir.
Jason Orendorff

1
Bunun yerine sabit kodlama bölgesinin $iv_size = 16;, ben kullanırım: $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("AES-256-CBC"))kullanılan şifre ile kullanım IV boyutu arasındaki bağlantıyı göstermek amacıyla. Ayrıca pkcs7_pad()/ ihtiyacını biraz genişletebilir (ya da etmeyebilir) pkcs7_unpad()ya da onlardan kurtularak ve "aes-256-ctr" kullanarak gönderiyi basitleştirebilirsiniz. Harika gönderi @ Ja͢ck
Patrick Allaert

24

Sanırım bunun daha önce cevaplandığını düşünüyorum ... ama yine de, verileri şifrelemek / şifresini çözmek istiyorsanız, SHA256'yı kullanamazsınız

//Key
$key = 'SuperSecretKey';

//To Encrypt:
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, 'I want to encrypt this', MCRYPT_MODE_ECB);

//To Decrypt:
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, MCRYPT_MODE_ECB);

7
Bunun için ECB'yi de kullanmamalısınız.
Maarten Bodewes

7
Anahtarlar rastgele bayt olmalı veya güvenli bir anahtar türetme işlevi kullanmalısınız.
Maarten Bodewes

4
MCRYPT_RIJNDAEL_256 standartlaştırılmış bir işlev değildir, AES kullanmalısınız (MCRYPT_RIJNDAEL_128)
Maarten Bodewes

14

Yanıt Arka Planı ve Açıklama

Bu soruyu anlamak için önce SHA256'nın ne olduğunu anlamalısınız. SHA256, bir Şifreleme Hash Fonksiyonudur . Şifreleme Karma İşlevi, çıktısı şifreleme açısından güvenli olan tek yönlü bir işlevdir. Bu, bir hash'i hesaplamanın kolay olduğu (verileri şifrelemeye eşdeğer), ancak hash kullanarak orijinal girdiyi elde etmenin zor olduğu (verilerin şifresini çözmeye eşdeğer) anlamına gelir. Şifreleme karma işlevi kullanmak, şifre çözmenin hesaplama açısından olanaksız olduğu anlamına geldiğinden, SHA256 ile şifre çözme işlemi gerçekleştiremezsiniz.

Kullanmak istediğiniz şey iki yönlü bir işlevdir, ancak daha spesifik olarak bir Blok Şifresidir . Verilerin hem şifrelenmesine hem de şifresinin çözülmesine izin veren bir işlev. İşlevler mcrypt_encryptve mcrypt_decryptvarsayılan olarak Blowfish algoritmasını kullanır. PHP'nin mcrypt kullanımı bu kılavuzda bulunabilir . Mcrypt'in kullandığı şifreyi seçmek için bir şifre tanımları listesi de mevcuttur. Blowfish ile ilgili bir wiki Wikipedia'da bulunabilir . Bir blok şifresi, girdiyi bilinen boyut ve konumdaki bloklar halinde bilinen bir anahtarla şifreler, böylece verilerin daha sonra anahtar kullanılarak şifresi çözülebilir. SHA256'nın size sağlayamayacağı şey budur.

kod

$key = 'ThisIsTheCipherKey';

$ciphertext = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, 'This is plaintext.', MCRYPT_MODE_CFB);

$plaintext = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $encrypted, MCRYPT_MODE_CFB);

Bunun için ECB'yi de kullanmamalısınız.
Maarten Bodewes

Anahtarlar rastgele bayt olmalı veya güvenli bir anahtar türetme işlevi kullanmalısınız.
Maarten Bodewes

4
ECB modunu asla kullanmayın. Güvensizdir ve çoğu zaman verileri gerçekten şifrelemeye gerçekten yardımcı olmaz (sadece şifrelemek yerine). Daha fazla bilgi için konuyla ilgili mükemmel Wikipedia makalesine bakın .
Holger Just

1
En iyisi mcrypt kullanmamaktır, yazılım terkedilmiştir, yıllardır güncellenmemiştir ve standart PKCS # 7 (née PKCS # 5) dolgusunu desteklememektedir, sadece ikili verilerle bile kullanılamayan standart dışı boş doldurma . mcrypt birçok seçkin vardı böcek yerine kullanmayı düşünün .. 2003 arkasını kalma Defuse , bu tutulan ve doğru olduğunu ediliyor.
zaph

9

İşte openssl_encrypt kullanan bir örnek

//Encryption:
$textToEncrypt = "My Text to Encrypt";
$encryptionMethod = "AES-256-CBC";
$secretHash = "encryptionhash";
$iv = mcrypt_create_iv(16, MCRYPT_RAND);
$encryptedText = openssl_encrypt($textToEncrypt,$encryptionMethod,$secretHash, 0, $iv);

//Decryption:
$decryptedText = openssl_decrypt($encryptedText, $encryptionMethod, $secretHash, 0, $iv);
print "My Decrypted Text: ". $decryptedText;

2
Bunun yerine şunu mcrypt_create_iv()kullanırdım:, openssl_random_pseudo_bytes(openssl_cipher_iv_length($encryptionMethod))bu şekilde metodoloji $ encryptionMethod'un herhangi bir değeri için çalışır ve yalnızca openssl uzantısını kullanır.
Patrick Allaert

Yukarıdaki kod falseiçin döner openssl_decrypt(). Bkz. Stackoverflow.com/q/41952509/1066234 AES gibi blok şifreleri, giriş verilerinin blok boyutunun tam katı olmasını gerektirdiğinden (AES için 16 bayt) doldurma gereklidir.
Kai Noack

6
     function my_simple_crypt( $string, $action = 'e' ) {
        // you may change these values to your own
        $secret_key = 'my_simple_secret_key';
        $secret_iv = 'my_simple_secret_iv';

        $output = false;
        $encrypt_method = "AES-256-CBC";
        $key = hash( 'sha256', $secret_key );
        $iv = substr( hash( 'sha256', $secret_iv ), 0, 16 );

        if( $action == 'e' ) {
            $output = base64_encode( openssl_encrypt( $string, $encrypt_method, $key, 0, $iv ) );
        }
        else if( $action == 'd' ){
            $output = openssl_decrypt( base64_decode( $string ), $encrypt_method, $key, 0, $iv );
        }

        return $output;
    }

Çok basit ! URL segmenti şifreleme-şifre çözme için kullanıyorum. Teşekkürler
Mahbub Tito

0

falseKullanırken openssl_decrypt()ve çalışırken şifreleme ve şifre çözme işleminin nasıl yapılacağını anlamam epey zaman aldı .

    // cryptographic key of a binary string 16 bytes long (because AES-128 has a key size of 16 bytes)
    $encryption_key = '58adf8c78efef9570c447295008e2e6e'; // example
    $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
    $encrypted = openssl_encrypt($plaintext, 'aes-256-cbc', $encryption_key, OPENSSL_RAW_DATA, $iv);
    $encrypted = $encrypted . ':' . base64_encode($iv);

    // decrypt to get again $plaintext
    $parts = explode(':', $encrypted);
    $decrypted = openssl_decrypt($parts[0], 'aes-256-cbc', $encryption_key, OPENSSL_RAW_DATA, base64_decode($parts[1])); 

Şifrelenmiş dizeyi bir URL aracılığıyla iletmek istiyorsanız, dizeyi yeniden kodlamanız gerekir:

    $encrypted = urlencode($encrypted);

Neler olduğunu daha iyi anlamak için şunu okuyun:

16 bayt uzunluğunda anahtarlar oluşturmak için şunları kullanabilirsiniz:

    $bytes = openssl_random_pseudo_bytes(16);
    $hex = bin2hex($bytes);

Openssl'nin hata mesajlarını görmek için şunları kullanabilirsiniz: echo openssl_error_string();

Umarım yardımcı olur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.