Şahsen ben mcrypt
yayınlanan diğerleri gibi kullanırdım . Ama dikkat edilmesi gereken çok şey var ...
PHP'de bir şifreyi nasıl şifreler ve şifresini çözerim?
Sizin için her şeyi halleden güçlü bir sınıf için aşağıya bakın:
Şifreleri şifrelemek için en güvenli algoritma nedir?
en güvenli ? herhangi biri. Şifreleyecekseniz en güvenli yöntem, bilginin açığa çıkması güvenlik açıklarına (XSS, uzaktan dahil etme vb.) Karşı korunmaktır. Çıkarsa, saldırgan sonunda şifrelemeyi kırabilir (hiçbir şifreleme anahtar olmadan% 100 geri alınamaz - @NullUserException bunun tamamen doğru olmadığına işaret eder. OneTimePad gibi kırılması imkansız olan bazı şifreleme şemaları vardır ) .
Özel anahtarı nerede saklayabilirim?
Yapacağım şey 3 anahtar kullanmak. Biri kullanıcı tarafından sağlanır, biri uygulamaya özel, diğeri kullanıcıya özgüdür (tuz gibi). Uygulamaya özel anahtar herhangi bir yerde saklanabilir (web kökünün dışındaki bir yapılandırma dosyasında, çevresel bir değişkende vb.). Kullanıcıya özel şifre şifrelenmiş parolanın yanındaki db sütununda saklanır. Sağlanan kullanıcı depolanmaz. Sonra böyle bir şey yapardın:
$key = $userKey . $serverKey . $userSuppliedKey;
Bunun yararı, verilerin hiçbirinden ödün verilmeksizin iki anahtardan herhangi birinin tehlikeye atılmasıdır. Bir SQL Injection saldırısı varsa, bunlar alabilirsiniz $userKey
, ancak diğer 2. Bir yerel sunucu istismar varsa, bunlar alabilirsiniz $userKey
ve $serverKey
üçü değil $userSuppliedKey
. Kullanıcıyı bir anahtarla dövürlerse, $userSuppliedKey
diğer 2'yi alamazlar (ancak daha sonra, kullanıcı bir anahtarla dövülürse, yine de çok geç kalırsınız).
Özel anahtarı saklamak yerine, şifreleri çözülmüş bir şifreye ihtiyaç duyduklarında kullanıcıların özel anahtarı girmelerini istemek iyi bir fikir mi? (Bu uygulamanın kullanıcılarına güvenilebilir)
Kesinlikle. Aslında, bunu yapmanın tek yolu bu. Aksi takdirde, şifrelenmemiş bir sürümü dayanıklı bir depolama biçiminde (APC veya memcached gibi paylaşılan bellek veya bir oturum dosyasında) saklamanız gerekir. Bu, kendinizi başka tavizlere maruz bırakıyor. Parolanın şifrelenmemiş sürümünü hiçbir zaman yerel değişken dışında bir yerde saklamayın.
Şifre hangi yollarla çalınabilir ve şifresi çözülebilir? Nelerden haberdar olmam gerekir?
Sistemlerinizin herhangi bir şekilde ele geçirilmesi şifrelenmiş verileri görüntülemelerine izin verir. Kod enjekte edebilir veya dosya sisteminize erişebilirlerse, şifresi çözülmüş verileri görüntüleyebilirler (verilerin şifresini çözen dosyaları düzenleyebildikleri için). Herhangi bir Replay veya MITM saldırısı biçimi de onlara ilgili anahtarlara tam erişim sağlayacaktır. Ham HTTP trafiğini koklamak da onlara anahtarlar verecektir.
Tüm trafik için SSL kullanın. Sunucudaki hiçbir şeyin herhangi bir güvenlik açığından (CSRF, XSS, SQL Enjeksiyonu, Ayrıcalık Yükselmesi, Uzaktan Kod Yürütme, vb.) Olmadığından emin olun.
Düzenleme: İşte güçlü bir şifreleme yöntemi bir PHP sınıfı uygulaması:
/**
* A class to handle secure encryption and decryption of arbitrary data
*
* Note that this is not just straight encryption. It also has a few other
* features in it to make the encrypted data far more secure. Note that any
* other implementations used to decrypt data will have to do the same exact
* operations.
*
* Security Benefits:
*
* - Uses Key stretching
* - Hides the Initialization Vector
* - Does HMAC verification of source data
*
*/
class Encryption {
/**
* @var string $cipher The mcrypt cipher to use for this instance
*/
protected $cipher = '';
/**
* @var int $mode The mcrypt cipher mode to use
*/
protected $mode = '';
/**
* @var int $rounds The number of rounds to feed into PBKDF2 for key generation
*/
protected $rounds = 100;
/**
* Constructor!
*
* @param string $cipher The MCRYPT_* cypher to use for this instance
* @param int $mode The MCRYPT_MODE_* mode to use for this instance
* @param int $rounds The number of PBKDF2 rounds to do on the key
*/
public function __construct($cipher, $mode, $rounds = 100) {
$this->cipher = $cipher;
$this->mode = $mode;
$this->rounds = (int) $rounds;
}
/**
* Decrypt the data with the provided key
*
* @param string $data The encrypted datat to decrypt
* @param string $key The key to use for decryption
*
* @returns string|false The returned string if decryption is successful
* false if it is not
*/
public function decrypt($data, $key) {
$salt = substr($data, 0, 128);
$enc = substr($data, 128, -64);
$mac = substr($data, -64);
list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key);
if (!hash_equals(hash_hmac('sha512', $enc, $macKey, true), $mac)) {
return false;
}
$dec = mcrypt_decrypt($this->cipher, $cipherKey, $enc, $this->mode, $iv);
$data = $this->unpad($dec);
return $data;
}
/**
* Encrypt the supplied data using the supplied key
*
* @param string $data The data to encrypt
* @param string $key The key to encrypt with
*
* @returns string The encrypted data
*/
public function encrypt($data, $key) {
$salt = mcrypt_create_iv(128, MCRYPT_DEV_URANDOM);
list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key);
$data = $this->pad($data);
$enc = mcrypt_encrypt($this->cipher, $cipherKey, $data, $this->mode, $iv);
$mac = hash_hmac('sha512', $enc, $macKey, true);
return $salt . $enc . $mac;
}
/**
* Generates a set of keys given a random salt and a master key
*
* @param string $salt A random string to change the keys each encryption
* @param string $key The supplied key to encrypt with
*
* @returns array An array of keys (a cipher key, a mac key, and a IV)
*/
protected function getKeys($salt, $key) {
$ivSize = mcrypt_get_iv_size($this->cipher, $this->mode);
$keySize = mcrypt_get_key_size($this->cipher, $this->mode);
$length = 2 * $keySize + $ivSize;
$key = $this->pbkdf2('sha512', $key, $salt, $this->rounds, $length);
$cipherKey = substr($key, 0, $keySize);
$macKey = substr($key, $keySize, $keySize);
$iv = substr($key, 2 * $keySize);
return array($cipherKey, $macKey, $iv);
}
/**
* Stretch the key using the PBKDF2 algorithm
*
* @see http://en.wikipedia.org/wiki/PBKDF2
*
* @param string $algo The algorithm to use
* @param string $key The key to stretch
* @param string $salt A random salt
* @param int $rounds The number of rounds to derive
* @param int $length The length of the output key
*
* @returns string The derived key.
*/
protected function pbkdf2($algo, $key, $salt, $rounds, $length) {
$size = strlen(hash($algo, '', true));
$len = ceil($length / $size);
$result = '';
for ($i = 1; $i <= $len; $i++) {
$tmp = hash_hmac($algo, $salt . pack('N', $i), $key, true);
$res = $tmp;
for ($j = 1; $j < $rounds; $j++) {
$tmp = hash_hmac($algo, $tmp, $key, true);
$res ^= $tmp;
}
$result .= $res;
}
return substr($result, 0, $length);
}
protected function pad($data) {
$length = mcrypt_get_block_size($this->cipher, $this->mode);
$padAmount = $length - strlen($data) % $length;
if ($padAmount == 0) {
$padAmount = $length;
}
return $data . str_repeat(chr($padAmount), $padAmount);
}
protected function unpad($data) {
$length = mcrypt_get_block_size($this->cipher, $this->mode);
$last = ord($data[strlen($data) - 1]);
if ($last > $length) return false;
if (substr($data, -1 * $last) !== str_repeat(chr($last), $last)) {
return false;
}
return substr($data, 0, -1 * $last);
}
}
Ben PHP 5.6 eklenen bir işlevi kullanıyorum unutmayın: hash_equals
. 5.6'nın altındaysanız, çift HMAC doğrulaması kullanarak zamanlama açısından güvenli bir karşılaştırma işlevi uygulayan bu ikame işlevini kullanabilirsiniz :
function hash_equals($a, $b) {
$key = mcrypt_create_iv(128, MCRYPT_DEV_URANDOM);
return hash_hmac('sha512', $a, $key) === hash_hmac('sha512', $b, $key);
}
Kullanımı:
$e = new Encryption(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$encryptedData = $e->encrypt($data, $key);
Sonra, şifresini çözmek için:
$e2 = new Encryption(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$data = $e2->decrypt($encryptedData, $key);
$e2
Farklı örneklerin hala verilerin şifresini çözeceğini göstermek için ikinci kez kullandığımı unutmayın .
Şimdi, nasıl çalışıyor / neden başka bir çözüm üzerinde kullanılıyor:
Anahtarlar
Tuşlar doğrudan kullanılmaz. Bunun yerine, anahtar standart bir PBKDF2 türevi ile uzatılır.
Şifreleme için kullanılan anahtar, şifrelenmiş her metin bloğu için benzersizdir. Dolayısıyla, verilen anahtar bir "ana anahtar" olur. Bu sınıf bu nedenle şifre ve kimlik doğrulama anahtarları için anahtar döndürme sağlar.
ÖNEMLİ NOT , $rounds
parametre yeterli güce sahip gerçek rastgele anahtarlar için yapılandırılmıştır (en az 128 bit Kriptografik Olarak Güvenli rastgele). Eğer bir şifre veya olmayan rastgele anahtar (CS Random veya daha az rastgele ardından 128 bit) kullanacağız varsa, gereken bu parametreyi arttırır. Şifreler için en az 10000 önerebilirim (ne kadar çok para harcarsanız, o kadar iyi, ancak çalışma zamanına eklenir) ...
Veri bütünlüğü
- Güncellenmiş sürüm, şifrelenmiş verilerin gerçekliğini sağlamak için çok daha iyi bir yöntem olan ENCRYPT-THEN-MAC kullanır.
Şifreleme:
- Şifrelemeyi gerçekleştirmek için mcrypt kullanır. Ben ya
MCRYPT_BLOWFISH
da MCRYPT_RIJNDAEL_128
cyphers ve MCRYPT_MODE_CBC
mod için kullanmanızı öneririm . Yeterince güçlü ve hala oldukça hızlı (bir şifreleme ve şifre çözme döngüsü makinemde yaklaşık 1/2 saniye sürüyor).
Şimdi, ilk listeden 3. maddeye gelince, bunun size vereceği şey şu şekildedir:
function makeKey($userKey, $serverKey, $userSuppliedKey) {
$key = hash_hmac('sha512', $userKey, $serverKey);
$key = hash_hmac('sha512', $key, $userSuppliedKey);
return $key;
}
İşlevde gerebilirsiniz makeKey()
, ancak daha sonra gerileceğinden, bunu yapmanın gerçekten büyük bir anlamı yoktur.
Depolama boyutuna gelince, düz metne bağlıdır. Blowfish 8 baytlık bir blok boyutu kullanır, bu nedenle:
- Tuz için 16 bayt
- Hmac için 64 bayt
- veri uzunluğu
- Veri uzunluğu% 8 == 0 olacak şekilde doldurma
Yani 16 karakterlik bir veri kaynağı için şifrelenecek 16 karakterlik veri olacaktır. Bu, gerçek şifreli veri boyutunun dolgudan dolayı 16 bayt olduğu anlamına gelir. Daha sonra tuz için 16 bayt ve hmac için 64 bayt ekleyin ve toplam depolanan boyut 96 bayttır. Yani en iyi 80 karakter yükü ve en kötü 87 karakter yükü var ...
Umarım bu yardımcı olur...
Not: 12/11/12: Bu sınıfı daha iyi türetilmiş anahtarlar kullanarak ve MAC üretimini düzelterek çok daha iyi bir şifreleme yöntemiyle güncelledim ...