Wichtig : Wenn Sie einen haben sehr speziellen Anwendungsfall, nicht verschlüsseln Passwörter , verwenden Sie stattdessen ein Kennwort - Hashing - Algorithmus. Wenn jemand sagt, dass er seine Passwörter in einer serverseitigen Anwendung verschlüsselt , ist er entweder nicht informiert oder beschreibt ein gefährliches Systemdesign. Das sichere Speichern von Passwörtern ist ein völlig anderes Problem als die Verschlüsselung.
Informiert werden. Entwerfen Sie sichere Systeme.
Portable Datenverschlüsselung in PHP
Wenn Sie PHP 5.4 oder neuer verwenden und kein Kryptografiemodul selbst schreiben möchten, empfehle ich die Verwendung einer vorhandenen Bibliothek, die authentifizierte Verschlüsselung bietet . Die Bibliothek, die ich verlinkt habe, basiert nur auf dem, was PHP bietet, und wird regelmäßig von einer Handvoll Sicherheitsforschern überprüft. (Mich eingenommen.)
Wenn Ihre Portabilität Ziele nicht verhindern PECL - Erweiterungen erfordern, libsodium ist hoch über alles , was Sie empfohlen oder ich kann in PHP schreiben.
Update (12.06.2016): Sie können jetzt Natrium_Kompat verwenden und dieselben Crypto Libsodium-Angebote verwenden, ohne PECL-Erweiterungen zu installieren.
Wenn Sie sich in der Kryptografietechnik versuchen möchten, lesen Sie weiter.
Zunächst sollten Sie sich die Zeit nehmen, um die Gefahren einer nicht authentifizierten Verschlüsselung und das Cryptographic Doom-Prinzip kennenzulernen .
- Verschlüsselte Daten können weiterhin von einem böswilligen Benutzer manipuliert werden.
- Durch die Authentifizierung der verschlüsselten Daten werden Manipulationen verhindert.
- Die Authentifizierung der unverschlüsselten Daten verhindert keine Manipulationen.
Verschlüsselung und Entschlüsselung
Die Verschlüsselung in PHP ist eigentlich einfach (wir werden sie verwenden openssl_encrypt()
und openssl_decrypt()
sobald Sie einige Entscheidungen zur Verschlüsselung Ihrer Informationen getroffen haben. openssl_get_cipher_methods()
Eine Liste der auf Ihrem System unterstützten Methoden finden Sie hier. Die beste Wahl ist AES im CTR-Modus :
aes-128-ctr
aes-192-ctr
aes-256-ctr
Derzeit gibt es keinen Grund zu der Annahme, dass die Größe des AES-Schlüssels ein wichtiges Problem darstellt (größer ist wahrscheinlich nicht besser, da die Schlüsselplanung im 256-Bit-Modus schlecht ist).
Hinweis: Wir verwenden es nicht, mcrypt
da es sich um Abbruchware handelt und nicht gepatchte Fehler aufweist , die möglicherweise die Sicherheit beeinträchtigen. Aus diesen Gründen ermutige ich andere PHP-Entwickler, dies ebenfalls zu vermeiden.
Einfacher Verschlüsselungs- / Entschlüsselungs-Wrapper mit 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;
}
}
Anwendungsbeispiel
$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);
Demo : https://3v4l.org/jl7qR
Die obige einfache Kryptobibliothek ist immer noch nicht sicher zu verwenden. Wir müssen Chiffretexte authentifizieren und überprüfen, bevor wir entschlüsseln .
Hinweis : Standardmäßig UnsafeCrypto::encrypt()
wird eine rohe Binärzeichenfolge zurückgegeben. Nennen Sie es so, wenn Sie es in einem binärsicheren Format (base64-codiert) speichern müssen:
$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);
Demo : http://3v4l.org/f5K93
Einfacher Authentifizierungs-Wrapper
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);
}
}
Anwendungsbeispiel
$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);
Demos : Raw Binary , Base64-codiert
Wenn jemand diese SaferCrypto
Bibliothek in einer Produktionsumgebung oder in Ihrer eigenen Implementierung derselben Konzepte verwenden möchte, empfehle ich dringend, sich vor Ihnen an Ihre ansässigen Kryptographen zu wenden, um eine zweite Meinung einzuholen. Sie können Ihnen von Fehlern erzählen, die mir vielleicht gar nicht bewusst sind.
Mit einer seriösen Kryptografie-Bibliothek sind Sie viel besser dran .