Was ist der einfachste Weg, um eine Zwei-Wege-Verschlüsselung in gängigen PHP-Installationen durchzuführen?
Ich muss in der Lage sein, Daten mit einem Zeichenfolgenschlüssel zu verschlüsseln und denselben Schlüssel zum Entschlüsseln am anderen Ende zu verwenden.
Die Sicherheit ist nicht so wichtig wie die Portabilität des Codes, daher möchte ich die Dinge so einfach wie möglich halten. Derzeit verwende ich eine RC4-Implementierung, aber wenn ich etwas finde, das nativ unterstützt wird, kann ich mir eine Menge unnötigen Code sparen.
Wichtig: Es sei denn, Sie haben eine sehr besonderer Anwendungsfall, Passwörter nicht verschlüsseln, verwenden Sie stattdessen einen Passwort-Hashing-Algorithmus. Wenn jemand sie sagt Verschlüsseln ihre Passwörter in einer serverseitigen Anwendung, sind sie entweder nicht informiert oder beschreiben ein gefährliches Systemdesign. Passwörter sicher speichern ist ein völlig anderes Problem als die Verschlüsselung.
Informiert werden. Entwerfen Sie sichere Systeme.
Verschlüsselung portabler Daten in PHP
Wenn Sie verwenden PHP 5.4 oder neuer und kein Kryptografiemodul selbst schreiben möchten, empfehle ich die Verwendung von eine vorhandene Bibliothek, die eine authentifizierte Verschlüsselung bereitstellt. Die Bibliothek, die ich verlinkt habe, stützt sich nur auf das, was PHP bietet, und wird regelmäßig von einer Handvoll Sicherheitsforschern überprüft. (Mich eingenommen.)
Wenn Ihre Portabilitätsziele nicht verhindern, dass PECL-Erweiterungen erforderlich sind, libnatrium ist höchst empfohlen über alles, was Sie oder ich in PHP schreiben können.
Aktualisierung (2016-06-12): Sie können jetzt verwenden Natrium_kompat und nutzen Sie die gleichen Krypto-Libsodium-Angebote, ohne PECL-Erweiterungen zu installieren.
Wenn Sie sich in der Kryptografietechnik versuchen möchten, lesen Sie weiter.
Zunächst sollten Sie sich Zeit nehmen, um zu lernen die Gefahren einer nicht authentifizierten Verschlüsselung und das kryptografische Doom-Prinzip.
- Verschlüsselte Daten können immer noch von einem böswilligen Benutzer manipuliert werden.
- Die Authentifizierung der verschlüsselten Daten verhindert Manipulationen.
- Die Authentifizierung der unverschlüsselten Daten verhindert keine Manipulation.
Verschlüsselung und Entschlüsselung
Die Verschlüsselung in PHP ist eigentlich einfach (wir werden verwenden openssl_encrypt()
und openssl_decrypt()
sobald Sie einige Entscheidungen darüber getroffen haben, wie Sie Ihre Informationen verschlüsseln. Konsultieren openssl_get_cipher_methods()
für eine Liste der auf Ihrem System unterstützten Methoden. Die beste Wahl ist AES im CTR-Modus:
aes-128-ctr
aes-192-ctr
aes-256-ctr
Es gibt derzeit keinen Grund zu der Annahme, dass die AES-Schlüsselgröße ist ein wichtiges Problem, über das man sich Sorgen machen muss (größer ist wahrscheinlich nicht besser, wegen schlechter Schlüsselplanung im 256-Bit-Modus).
Notiz: Wir verwenden nicht mcrypt
denn es ist Abbruchware und hat Ungepatchte Fehler das könnte sicherheitsrelevant sein. Aus diesen Gründen ermutige ich andere PHP-Entwickler, es 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 verifizieren, bevor wir sie entschlüsseln.
Notiz: Standardmäßig, UnsafeCrypto::encrypt()
gibt einen rohen binären String zurück. 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 Authentifizierungswrapper
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: rohe Binärdatei, Base64-kodiert
Falls das jemand nutzen möchte SaferCrypto
Bibliothek in einer Produktionsumgebung oder Ihre eigene Implementierung der gleichen Konzepte, empfehle ich dringend, sich an zu wenden Ihre ansässigen Kryptografen Holen Sie sich vorher eine zweite Meinung ein. Sie können Ihnen Fehler aufzeigen, die mir vielleicht gar nicht bewusst sind.
Sie werden viel besser dran sein, wenn Sie es verwenden eine seriöse Kryptografie-Bibliothek.
Verschlüsseln mit openssl_encrypt() Die Funktion openssl_encrypt bietet eine sichere und einfache Möglichkeit, Ihre Daten zu verschlüsseln.
Im folgenden Skript verwenden wir die AES128-Verschlüsselungsmethode, aber Sie können je nachdem, was Sie verschlüsseln möchten, eine andere Art von Verschlüsselungsmethode in Betracht ziehen.
<?php
$message_to_encrypt = "Yoroshikune";
$secret_key = "my-secret-key";
$method = "aes128";
$iv_length = openssl_cipher_iv_length($method);
$iv = openssl_random_pseudo_bytes($iv_length);
$encrypted_message = openssl_encrypt($message_to_encrypt, $method, $secret_key, 0, $iv);
echo $encrypted_message;
?>
Hier ist eine Erklärung der verwendeten Variablen:
message_to_encrypt : die Daten, die Sie verschlüsseln möchten secret_key : Dies ist Ihr „Passwort“ für die Verschlüsselung. Achten Sie darauf, nichts zu Einfaches zu wählen und achten Sie darauf, Ihren geheimen Schlüssel nicht mit anderen Personen zu teilen. Methode: die Verschlüsselungsmethode. Hier haben wir uns für AES128 entschieden. iv_length und iv : Bereiten Sie die Verschlüsselung mit Bytes vor verschlüsselte_Nachricht : die Variable, die Ihre verschlüsselte Nachricht enthält
Entschlüsseln mit openssl_decrypt() Nachdem Sie Ihre Daten verschlüsselt haben, müssen Sie sie möglicherweise entschlüsseln, um die Nachricht wiederzuverwenden, die Sie zuerst in eine Variable eingefügt haben. Dazu verwenden wir die Funktion openssl_decrypt().
<?php
$message_to_encrypt = "Yoroshikune";
$secret_key = "my-secret-key";
$method = "aes128";
$iv_length = openssl_cipher_iv_length($method);
$iv = openssl_random_pseudo_bytes($iv_length);
$encrypted_message = openssl_encrypt($message_to_encrypt, $method, $secret_key, 0, $iv);
$decrypted_message = openssl_decrypt($encrypted_message, $method, $secret_key, 0, $iv);
echo $decrypted_message;
?>
Die von openssl_decrypt() vorgeschlagene Entschlüsselungsmethode ist in der Nähe von openssl_encrypt().
Der einzige Unterschied besteht darin, dass Sie anstelle von $message_to_encrypt Ihre bereits verschlüsselte Nachricht als erstes Argument von openssl_decrypt() hinzufügen müssen.
Hinweis: Der geheime Schlüssel und iv müssen zum Entschlüsseln gespeichert werden.
PHP7.2 komplett weggezogen Mcrypt
und die Verschlüsselung basiert nun auf dem Wartbaren Libsodium
Bücherei.
Alle Ihre Verschlüsselungsanforderungen können grundsätzlich durch gelöst werden Libsodium
Bücherei.
// On Alice's computer:
$msg = 'This comes from Alice.';
$signed_msg = sodium_crypto_sign($msg, $secret_sign_key);
// On Bob's computer:
$original_msg = sodium_crypto_sign_open($signed_msg, $alice_sign_publickey);
if ($original_msg === false) {
throw new Exception('Invalid signature');
} else {
echo $original_msg; // Displays "This comes from Alice."
}
Libsodium-Dokumentation: https://github.com/paragonie/pecl-libsodium-doc
WICHTIG Diese Antwort gilt nur für PHP 5, verwenden Sie in PHP 7 integrierte kryptografische Funktionen.
Hier ist eine einfache, aber sichere Implementierung:
- AES-256-Verschlüsselung im CBC-Modus
- PBKDF2 zum Erstellen eines Verschlüsselungsschlüssels aus einem Klartextpasswort
- HMAC, um die verschlüsselte Nachricht zu authentifizieren.
Code und Beispiele finden Sie hier: https://stackoverflow.com/a/19445173/1387163
XOR einfach deine Zeichenfolge.
– Thunraz
13. Februar 2012 um 14:28 Uhr
Verwenden Sie für allgemeine Verschlüsselungen entschärfen/php-verschlüsselung/ anstatt selbst zu rollen.
– Scott Arciszewski
11. Mai 2015 um 4:37 Uhr
Hände weg von github.com/defuse/php-verschlüsselung – Es ist um Größenordnungen langsamer als mcrypt.
– Eugen Rieck
11. Mai 2015 um 16:12 Uhr
@Scott Das Denken nach dem Motto “Das wird wahrscheinlich nicht der Engpass sein” hat uns eine Menge schlechter Software gebracht.
– Eugen Rieck
12. Mai 2015 um 14:30 Uhr
Wenn Sie wirklich viele Daten bis zu dem Punkt verschlüsseln/entschlüsseln, dass die Millisekunden, die es kostet, Ihre Anwendung verstopfen, beißen Sie in den sauren Apfel und wechseln Sie zu libsodium.
Sodium::crypto_secretbox()
undSodium::crypto_secretbox_open()
sind sicher und performant.– Scott Arciszewski
12. Mai 2015 um 18:53 Uhr