Bevor Sie weitere Schritte ausführen, sollten Sie den Unterschied zwischen Verschlüsselung und Authentifizierung verstehen und wissen , warum Sie wahrscheinlich eine authentifizierte Verschlüsselung und nicht nur eine Verschlüsselung wünschen .
Um eine authentifizierte Verschlüsselung zu implementieren, möchten Sie dann MAC verschlüsseln. Die Reihenfolge der Verschlüsselung und Authentifizierung ist sehr wichtig! Eine der vorhandenen Antworten auf diese Frage machte diesen Fehler; ebenso wie viele in PHP geschriebene Kryptographie-Bibliotheken.
Sie sollten die Implementierung Ihrer eigenen Kryptografie vermeiden und stattdessen eine sichere Bibliothek verwenden, die von Kryptografieexperten geschrieben und überprüft wurde.
Update: PHP 7.2 bietet jetzt libsodium ! Aktualisieren Sie Ihre Systeme für beste Sicherheit auf PHP 7.2 oder höher und befolgen Sie nur die libsodium-Hinweise in dieser Antwort.
Verwenden Sie libsodium, wenn Sie PECL-Zugriff haben (oder natrium_compat, wenn Sie libsodium ohne PECL möchten). sonst ...
Verwenden Sie Defuse / PHP-Verschlüsselung ; Rollen Sie nicht Ihre eigene Kryptographie!
Beide oben verlinkten Bibliotheken machen es einfach und problemlos, authentifizierte Verschlüsselung in Ihren eigenen Bibliotheken zu implementieren.
Wenn Sie dennoch Ihre eigene Kryptografiebibliothek schreiben und bereitstellen möchten, müssen Sie diese Schritte unternehmen, die der herkömmlichen Weisheit jedes Kryptografieexperten im Internet widersprechen.
Verschlüsselung:
- Verschlüsseln Sie mit AES im CTR-Modus. Sie können auch GCM verwenden (wodurch die Notwendigkeit eines separaten MAC entfällt). Darüber hinaus sind ChaCha20 und Salsa20 (von libsodium bereitgestellt ) Stream-Chiffren und benötigen keine speziellen Modi.
- Sofern Sie oben nicht GCM ausgewählt haben, sollten Sie den Chiffretext mit HMAC-SHA-256 authentifizieren (oder für die Stream-Chiffren mit Poly1305 - die meisten libsodium-APIs tun dies für Sie). Der MAC sollte sowohl die IV als auch den Chiffretext abdecken!
Entschlüsselung:
- Wenn Poly1305 oder GCM nicht verwendet wird, berechnen Sie den MAC des Chiffretextes neu und vergleichen Sie ihn mit dem MAC, der mit gesendet wurde
hash_equals()
. Wenn dies fehlschlägt, brechen Sie den Vorgang ab.
- Entschlüsseln Sie die Nachricht.
Weitere Überlegungen zum Design:
- Komprimieren Sie niemals etwas. Chiffretext ist nicht komprimierbar; Das Komprimieren von Klartext vor der Verschlüsselung kann zu Informationslecks führen (z. B. CRIME und BREACH auf TLS).
- Stellen Sie sicher, dass Sie
mb_strlen()
und mb_substr()
den '8bit'
Zeichensatzmodus verwenden, um mbstring.func_overload
Probleme zu vermeiden .
- IVs sollten mit einem CSPRNG generiert werden . Wenn Sie verwenden
mcrypt_create_iv()
, NICHT VERWENDENMCRYPT_RAND
!
- Wenn Sie kein AEAD-Konstrukt verwenden, verschlüsseln Sie IMMER MAC!
bin2hex()
, base64_encode()
usw. können Informationen über Ihre Verschlüsselungsschlüssel über das Cache-Timing verlieren. Vermeiden Sie sie wenn möglich.
Selbst wenn Sie den hier gegebenen Ratschlägen folgen, kann bei der Kryptografie viel schief gehen. Lassen Sie Ihre Implementierung immer von einem Kryptografieexperten überprüfen. Wenn Sie nicht das Glück haben, mit einem Kryptografiestudenten an Ihrer örtlichen Universität persönlich befreundet zu sein, können Sie sich jederzeit im Cryptography Stack Exchange- Forum beraten lassen.
Wenn Sie eine professionelle Analyse Ihrer Implementierung benötigen, können Sie jederzeit ein seriöses Team von Sicherheitsberatern beauftragen, um Ihren PHP-Kryptografiecode zu überprüfen (Offenlegung: mein Arbeitgeber).
Wichtig: Wann keine Verschlüsselung verwendet werden soll
Kennwörter nicht verschlüsseln . Sie wollen Hash sie stattdessen eine dieser Passwort-HashingAlgorithmen:
Verwenden Sie niemals eine universelle Hash-Funktion (MD5, SHA256) zur Kennwortspeicherung.
Verschlüsseln Sie keine URL-Parameter . Es ist das falsche Werkzeug für den Job.
Beispiel für die Verschlüsselung von PHP-Zeichenfolgen mit Libsodium
Wenn Sie PHP <7.2 verwenden oder auf andere Weise libsodium nicht installiert haben, können Sie Natrium_Kompat verwenden , um dasselbe Ergebnis zu erzielen (wenn auch langsamer).
<?php
declare(strict_types=1);
/**
* Encrypt a message
*
* @param string $message - message to encrypt
* @param string $key - encryption key
* @return string
* @throws RangeException
*/
function safeEncrypt(string $message, string $key): string
{
if (mb_strlen($key, '8bit') !== SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
throw new RangeException('Key is not the correct size (must be 32 bytes).');
}
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$cipher = base64_encode(
$nonce.
sodium_crypto_secretbox(
$message,
$nonce,
$key
)
);
sodium_memzero($message);
sodium_memzero($key);
return $cipher;
}
/**
* Decrypt a message
*
* @param string $encrypted - message encrypted with safeEncrypt()
* @param string $key - encryption key
* @return string
* @throws Exception
*/
function safeDecrypt(string $encrypted, string $key): string
{
$decoded = base64_decode($encrypted);
$nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
$ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
$plain = sodium_crypto_secretbox_open(
$ciphertext,
$nonce,
$key
);
if (!is_string($plain)) {
throw new Exception('Invalid MAC');
}
sodium_memzero($ciphertext);
sodium_memzero($key);
return $plain;
}
Dann um es auszuprobieren:
<?php
// This refers to the previous code block.
require "safeCrypto.php";
// Do this once then store it somehow:
$key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
$message = 'We are all living in a yellow submarine';
$ciphertext = safeEncrypt($message, $key);
$plaintext = safeDecrypt($ciphertext, $key);
var_dump($ciphertext);
var_dump($plaintext);
Halit - Libsodium leichter gemacht
Eines der Projekte, an denen ich gearbeitet habe, ist eine Verschlüsselungsbibliothek namens Halite , die darauf abzielt, libsodium einfacher und intuitiver zu machen.
<?php
use \ParagonIE\Halite\KeyFactory;
use \ParagonIE\Halite\Symmetric\Crypto as SymmetricCrypto;
// Generate a new random symmetric-key encryption key. You're going to want to store this:
$key = new KeyFactory::generateEncryptionKey();
// To save your encryption key:
KeyFactory::save($key, '/path/to/secret.key');
// To load it again:
$loadedkey = KeyFactory::loadEncryptionKey('/path/to/secret.key');
$message = 'We are all living in a yellow submarine';
$ciphertext = SymmetricCrypto::encrypt($message, $key);
$plaintext = SymmetricCrypto::decrypt($ciphertext, $key);
var_dump($ciphertext);
var_dump($plaintext);
Die gesamte zugrunde liegende Kryptographie wird von libsodium verwaltet.
Beispiel mit Defuse / PHP-Verschlüsselung
<?php
/**
* This requires https://github.com/defuse/php-encryption
* php composer.phar require defuse/php-encryption
*/
use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;
require "vendor/autoload.php";
// Do this once then store it somehow:
$key = Key::createNewRandomKey();
$message = 'We are all living in a yellow submarine';
$ciphertext = Crypto::encrypt($message, $key);
$plaintext = Crypto::decrypt($ciphertext, $key);
var_dump($ciphertext);
var_dump($plaintext);
Hinweis : Crypto::encrypt()
Gibt eine hexadezimal codierte Ausgabe zurück.
Verwaltung von Verschlüsselungsschlüsseln
Wenn Sie versucht sind, ein "Passwort" zu verwenden, hören Sie sofort auf. Sie benötigen einen zufälligen 128-Bit-Verschlüsselungsschlüssel, kein menschliches, einprägsames Passwort.
Sie können einen Verschlüsselungsschlüssel für die langfristige Verwendung wie folgt speichern:
$storeMe = bin2hex($key);
Und auf Anfrage können Sie es wie folgt abrufen:
$key = hex2bin($storeMe);
Ich empfehle dringend , nur einen zufällig generierten Schlüssel für die langfristige Verwendung anstelle eines Kennworts als Schlüssel zu speichern (oder den Schlüssel abzuleiten).
Wenn Sie die Bibliothek von Defuse verwenden:
"Aber ich möchte wirklich ein Passwort verwenden."
Das ist eine schlechte Idee, aber okay, so geht's sicher.
Generieren Sie zunächst einen zufälligen Schlüssel und speichern Sie ihn in einer Konstanten.
/**
* Replace this with your own salt!
* Use bin2hex() then add \x before every 2 hex characters, like so:
*/
define('MY_PBKDF2_SALT', "\x2d\xb7\x68\x1a\x28\x15\xbe\x06\x33\xa0\x7e\x0e\x8f\x79\xd5\xdf");
Beachten Sie, dass Sie zusätzliche Arbeit hinzufügen und diese Konstante einfach als Schlüssel verwenden können, um sich viel Herzschmerz zu ersparen!
Verwenden Sie dann PBKDF2 (wie folgt), um einen geeigneten Verschlüsselungsschlüssel aus Ihrem Passwort abzuleiten, anstatt direkt mit Ihrem Passwort zu verschlüsseln.
/**
* Get an AES key from a static password and a secret salt
*
* @param string $password Your weak password here
* @param int $keysize Number of bytes in encryption key
*/
function getKeyFromPassword($password, $keysize = 16)
{
return hash_pbkdf2(
'sha256',
$password,
MY_PBKDF2_SALT,
100000, // Number of iterations
$keysize,
true
);
}
Verwenden Sie nicht nur ein 16-stelliges Passwort. Ihr Verschlüsselungsschlüssel wird komisch kaputt gehen.