Wie verschlüsselt / entschlüsselt man Daten in PHP?

110

Ich bin zurzeit Student und studiere PHP. Ich versuche, Daten in PHP einfach zu verschlüsseln / entschlüsseln. Ich habe online recherchiert und einige waren ziemlich verwirrend (zumindest für mich).

Folgendes versuche ich zu tun:

Ich habe eine Tabelle bestehend aus diesen Feldern (Benutzer-ID, F-Name, L-Name, E-Mail, Passwort)

Was ich haben möchte, ist, dass alle Felder verschlüsselt und dann entschlüsselt werden (Ist es möglich, sie zu verwenden? sha256 für die Verschlüsselung / Entschlüsselung zu verwenden, wenn nicht irgendein Verschlüsselungsalgorithmus)?

Eine andere Sache, die ich lernen möchte, ist, wie man eine Einbahnstraße hash(sha256)kombiniert mit einem guten "Salz" schafft . (Grundsätzlich möchte ich nur eine einfache Implementierung von Verschlüsselung / Entschlüsselung haben, hash(sha256)+salt) Sir / Ma'am. Ihre Antworten wären eine große Hilfe und würden sehr geschätzt. Vielen Dank ++

Randel Ramirez
quelle
9
SHA ist ein Hash, keine Verschlüsselung. Der entscheidende Punkt ist, dass ein Hash nicht auf die ursprünglichen Daten zurückgesetzt werden kann (jedenfalls nicht einfach). Sie möchten wahrscheinlich mcrypt oder wenn es nicht verfügbar ist, würde ich phpseclib empfehlen - obwohl es wichtig ist zu beachten, dass jede reine PHP-Implementierung von allem, was viel Mathematik auf niedriger Ebene beinhaltet, langsam ist ... Deshalb mag ich phpseclib, weil Es verwendet zuerst mcrypt, wenn es verfügbar ist, und greift nur als letzten Ausweg auf PHP-Implementierungen zurück.
Dave Random
7
Normalerweise möchten Sie kein Passwort entschlüsseln können!
Ja͢ck
1
Grundsätzlich sollten Sie nicht an Verschlüsselung auf dieser Ebene denken, sondern an Zugriffskontrolle, Vertraulichkeit, Integrität und Authentifizierung. Überprüfen Sie anschließend, wie Sie dies erreichen können, möglicherweise mithilfe von Verschlüsselung oder sicherem Hashing. Möglicherweise möchten Sie in PBKDF2 und bcrypt / scrypt lesen, um das sichere Hashing von Kennwörtern und dergleichen zu verstehen.
Maarten Bodewes

Antworten:

289

Vorwort

Beginnend mit Ihrer Tabellendefinition:

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

Hier sind die Änderungen:

  1. Die Felder Fname, Lnameund Emailwerden verschlüsselt eine symmetrische Chiffre, bereitgestellt von OpenSSL ,
  2. Das IVFeld speichert den Initialisierungsvektor für die Verschlüsselung verwendeten . Die Speicheranforderungen hängen von der verwendeten Verschlüsselung und dem verwendeten Modus ab. dazu später mehr.
  3. Das PasswordFeld wird mit einem Einweg- Passwort-Hash gehasht.

Verschlüsselung

Chiffre und Modus

Die Auswahl der besten Verschlüsselung und des besten Verschlüsselungsmodus geht über den Rahmen dieser Antwort hinaus. Die endgültige Auswahl wirkt sich jedoch auf die Größe des Verschlüsselungsschlüssels und des Initialisierungsvektors aus. Für diesen Beitrag verwenden wir AES-256-CBC mit einer festen Blockgröße von 16 Byte und einer Schlüsselgröße von 16, 24 oder 32 Byte.

Verschlüsselungsschlüssel

Ein guter Verschlüsselungsschlüssel ist ein binärer Blob, der von einem zuverlässigen Zufallszahlengenerator generiert wird. Das folgende Beispiel wird empfohlen (> = 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

Dies kann ein- oder mehrmals erfolgen (wenn Sie eine Kette von Verschlüsselungsschlüsseln erstellen möchten). Halten Sie diese so privat wie möglich.

IV

Der Initialisierungsvektor fügt der Verschlüsselung Zufälligkeit hinzu und ist für den CBC-Modus erforderlich. Diese Werte sollten idealerweise nur einmal verwendet werden (technisch einmal pro Verschlüsselungsschlüssel), daher sollte eine Aktualisierung eines beliebigen Teils einer Zeile diese neu generieren.

Eine Funktion hilft Ihnen beim Generieren der IV:

$iv_size = 16; // 128 bits
$iv = openssl_random_pseudo_bytes($iv_size, $strong);

Beispiel

Verschlüsseln wir das Namensfeld mit dem früheren $encryption_keyund $iv; Dazu müssen wir unsere Daten auf die Blockgröße auffüllen:

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
);

Lagerungssansprüche

Die verschlüsselte Ausgabe ist wie die IV binär; Das Speichern dieser Werte in einer Datenbank kann unter Verwendung bestimmter Spaltentypen wie BINARYoder erfolgenVARBINARY .

Der Ausgabewert ist wie der IV binär; Um diese Werte in MySQL zu speichern, sollten Sie BINARYoderVARBINARY Spalten verwenden. Wenn dies keine Option ist, können Sie die Binärdaten auch mit base64_encode()oder in eine Textdarstellung konvertieren. Dies bin2hex()erfordert zwischen 33% und 100% mehr Speicherplatz.

Entschlüsselung

Die Entschlüsselung der gespeicherten Werte ist ähnlich:

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
));

Authentifizierte Verschlüsselung

Sie können die Integrität des generierten Chiffretextes weiter verbessern, indem Sie eine Signatur anhängen, die aus einem geheimen Schlüssel (der sich vom Verschlüsselungsschlüssel unterscheidet) und dem Chiffretext generiert wird. Bevor der Chiffretext entschlüsselt wird, wird zuerst die Signatur überprüft (vorzugsweise mit einer zeitkonstanten Vergleichsmethode).

Beispiel

// 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
}

Siehe auch: hash_equals()

Hashing

Das Speichern eines umkehrbaren Passworts in Ihrer Datenbank muss so weit wie möglich vermieden werden. Sie möchten nur das Passwort überprüfen, anstatt dessen Inhalt zu kennen. Wenn ein Benutzer sein Passwort verliert, ist es besser, ihm das Zurücksetzen zu erlauben, als ihm sein ursprüngliches Passwort zu senden (stellen Sie sicher, dass das Zurücksetzen des Passworts nur für eine begrenzte Zeit möglich ist).

Das Anwenden einer Hash-Funktion ist eine Einwegoperation. Danach kann es sicher zur Überprüfung verwendet werden, ohne die Originaldaten preiszugeben. Für Passwörter ist eine Brute-Force-Methode aufgrund ihrer relativ kurzen Länge und der schlechten Passwortauswahl vieler Menschen ein praktikabler Ansatz, um sie aufzudecken.

Hashing-Algorithmen wie MD5 oder SHA1 wurden erstellt, um den Dateiinhalt anhand eines bekannten Hashwerts zu überprüfen. Sie sind stark optimiert, um diese Überprüfung so schnell wie möglich und dennoch genau durchzuführen. Aufgrund ihres relativ begrenzten Ausgabebereichs war es einfach, eine Datenbank mit bekannten Passwörtern und ihren jeweiligen Hash-Ausgaben, den Regenbogentabellen, zu erstellen.

Das Hinzufügen eines Salt zum Kennwort vor dem Hashing würde eine Regenbogentabelle unbrauchbar machen, aber die jüngsten Hardware-Fortschritte machten Brute-Force-Lookups zu einem praktikablen Ansatz. Deshalb benötigen Sie einen Hashing-Algorithmus, der bewusst langsam und einfach nicht zu optimieren ist. Es sollte auch in der Lage sein, die Last für schnellere Hardware zu erhöhen, ohne die Fähigkeit zu beeinträchtigen, vorhandene Kennwort-Hashes zu überprüfen, um sie zukunftssicher zu machen.

Derzeit stehen zwei beliebte Optionen zur Verfügung:

  1. PBKDF2 (Passwortbasierte Schlüsselableitungsfunktion v2)
  2. bcrypt (auch bekannt als Blowfish)

Diese Antwort verwendet ein Beispiel mit bcrypt.

Generation

Ein Passwort-Hash kann folgendermaßen generiert werden:

$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);

Das Salz wird mit erzeugt openssl_random_pseudo_bytes(), um einen zufälligen Datenklumpen zu bilden, der dann durchlaufen wird base64_encode()und strtr()mit dem erforderlichen Alphabet von übereinstimmt [A-Za-z0-9/.].

Die crypt()Funktion führt das Hashing basierend auf dem Algorithmus durch ($2y$ für Blowfish), dem Kostenfaktor (ein Faktor von 13 dauert auf einer 3-GHz-Maschine ungefähr 0,40 Sekunden) und dem Salz von 22 Zeichen durch.

Validierung

Nachdem Sie die Zeile mit den Benutzerinformationen abgerufen haben, überprüfen Sie das Kennwort auf folgende Weise:

$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;
}

Um ein Passwort zu überprüfen, rufen Sie crypt()erneut an, übergeben jedoch den zuvor berechneten Hash als Salt-Wert. Der Rückgabewert ergibt den gleichen Hash, wenn das angegebene Passwort mit dem Hash übereinstimmt. Um den Hash zu überprüfen, wird häufig empfohlen, eine Vergleichsfunktion mit konstanter Zeit zu verwenden, um Timing-Angriffe zu vermeiden.

Passwort-Hashing mit PHP 5.5

PHP 5.5 führte die Passwort-Hashing-Funktionen ein , mit denen Sie die oben beschriebene Hashing-Methode vereinfachen können:

$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 13]);

Und überprüfen:

if (password_verify($given_password, $db_hash)) {
    // password valid
}

Siehe auch : password_hash(),password_verify()

Jack
quelle
Welche Länge sollte ich verwenden, um Name, Nachname, E-Mail usw. für die sicherste Wette zu speichern? varbinary (???)
BentCoder
2
Sicher, aber es hängt davon ab, wie es verwendet wird. Wenn Sie eine Verschlüsselungsbibliothek veröffentlichen, wissen Sie nicht, wie Entwickler sie implementieren werden. Aus diesem Grund bietet github.com/defuse/php-encryption eine authentifizierte Verschlüsselung mit symmetrischen Schlüsseln und lässt Entwickler diese nicht schwächen, ohne den Code zu bearbeiten.
Scott Arciszewski
2
@Scott Sehr gut, ich habe ein Beispiel für authentifizierte Verschlüsselung hinzugefügt. danke für den Push :)
Ja͢ck
1
+1 für authentifizierte Verschlüsselung. Die Frage enthält nicht genügend Informationen, um zu sagen, dass AE hier nicht erforderlich ist. Sicherlich geht der SQL-Verkehr häufig über ein Netzwerk mit unbekannten Sicherheitseigenschaften, ebenso wie der Verkehr von der Datenbank zum Speicher. Backups und Replikation auch. Was ist das Bedrohungsmodell? Die Frage sagt nichts aus, und es könnte gefährlich sein, Annahmen zu treffen.
Jason Orendorff
1
Anstelle von Hardcodierung $iv_size = 16;würde ich verwenden: $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("AES-256-CBC"))um die Verbindung zwischen der Größe von iv anzugeben, die mit der verwendeten Chiffre verwendet werden soll. Sie können auch die Notwendigkeit (oder nicht) von pkcs7_pad()/ pkcs7_unpad()etwas erweitern oder einfach den Beitrag vereinfachen, indem Sie sie entfernen und "aes-256-ctr" verwenden. Großartiger Beitrag @ Ja͢ck
Patrick Allaert
24

Ich denke, dies wurde schon einmal beantwortet ... aber wenn Sie Daten verschlüsseln / entschlüsseln möchten, können Sie SHA256 nicht verwenden

//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);
romo
quelle
7
Sie sollten die EZB auch nicht verwenden.
Maarten Bodewes
7
Schlüssel sollten zufällige Bytes sein, oder Sie sollten eine sichere Schlüsselableitungsfunktion verwenden.
Maarten Bodewes
4
MCRYPT_RIJNDAEL_256 ist keine standardisierte Funktion, Sie sollten AES (MCRYPT_RIJNDAEL_128)
Maarten Bodewes
14

Antwort Hintergrund und Erklärung

Um diese Frage zu verstehen, müssen Sie zuerst verstehen, was SHA256 ist. SHA256 ist eine kryptografische Hash-Funktion . Eine kryptografische Hash-Funktion ist eine Einwegfunktion, deren Ausgabe kryptografisch sicher ist. Dies bedeutet, dass es einfach ist, einen Hash zu berechnen (entspricht dem Verschlüsseln von Daten), aber es ist schwierig, die ursprüngliche Eingabe mithilfe des Hash zu erhalten (entspricht dem Entschlüsseln der Daten). Da die Verwendung einer kryptografischen Hash-Funktion bedeutet, dass das Entschlüsseln rechnerisch nicht möglich ist, können Sie mit SHA256 keine Entschlüsselung durchführen.

Was Sie verwenden möchten, ist eine Zwei-Wege-Funktion, genauer gesagt eine Block-Verschlüsselung . Eine Funktion, die sowohl die Ver- als auch die Entschlüsselung von Daten ermöglicht. Die Funktionen mcrypt_encryptund mcrypt_decryptstandardmäßig verwenden den Blowfish-Algorithmus. Die Verwendung von mcrypt durch PHP finden Sie in diesem Handbuch . Es gibt auch eine Liste von Verschlüsselungsdefinitionen zur Auswahl der Verschlüsselung, die mcrypt verwendet. Ein Wiki zu Blowfish finden Sie bei Wikipedia . Eine Blockverschlüsselung verschlüsselt die Eingabe in Blöcken bekannter Größe und Position mit einem bekannten Schlüssel, damit die Daten später mit dem Schlüssel entschlüsselt werden können. Dies kann SHA256 Ihnen nicht bieten.

Code

$key = 'ThisIsTheCipherKey';

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

$plaintext = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $encrypted, MCRYPT_MODE_CFB);
Cytinus
quelle
Sie sollten die EZB auch nicht verwenden.
Maarten Bodewes
Schlüssel sollten zufällige Bytes sein, oder Sie sollten eine sichere Schlüsselableitungsfunktion verwenden.
Maarten Bodewes
4
Verwenden Sie niemals den EZB-Modus. Es ist unsicher und hilft meistens nicht wirklich dabei, die Daten tatsächlich zu verschlüsseln (anstatt sie nur zu verschlüsseln). Weitere Informationen finden Sie im ausgezeichneten Wikipedia-Artikel zu diesem Thema .
Holger gerade
1
Es ist am besten, mcrypt nicht zu verwenden, es ist Abbruchware, wurde seit Jahren nicht mehr aktualisiert und unterstützt kein Standard-PKCS # 7-Padding (geb. PKCS # 5), sondern nur nicht standardmäßiges Null-Padding, das nicht einmal mit Binärdaten verwendet werden kann . mcrypt hatte viele ausstehende Fehler aus dem Jahr 2003. Stattdessen sollten Sie die Verwendung der Entschärfung in Betracht ziehen , diese wird beibehalten und ist korrekt.
Zaph
9

Hier ist ein Beispiel mit openssl_encrypt

//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;
Vivek
quelle
2
Stattdessen mcrypt_create_iv()würde ich verwenden : openssl_random_pseudo_bytes(openssl_cipher_iv_length($encryptionMethod)), auf diese Weise funktioniert die Methodik für jeden Wert von $ encryptionMethod und würde nur die openssl-Erweiterung verwenden.
Patrick Allaert
Der obige Code gibt falsefür zurück openssl_decrypt(). Siehe stackoverflow.com/q/41952509/1066234 Da für Blockchiffren wie AES Eingabedaten erforderlich sind, muss ein genaues Vielfaches der Blockgröße (16 Byte für AES) aufgefüllt werden.
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;
    }
gauravbhai daxini
quelle
sehr einfach ! Ich benutze es für die URL-Segment-Verschlüsselung-Entschlüsselung. Vielen Dank
Mahbub Tito
0

Es hat eine ganze Weile gedauert, bis ich herausgefunden hatte, wie ich falsebei der Verwendung keine bekommen openssl_decrypt()und verschlüsseln und entschlüsseln kann.

    // 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])); 

Wenn Sie die verschlüsselte Zeichenfolge über eine URL übergeben möchten, müssen Sie die Zeichenfolge mit einem Urlencode versehen:

    $encrypted = urlencode($encrypted);

Um besser zu verstehen, was los ist, lesen Sie:

Um 16 Byte lange Schlüssel zu generieren, können Sie Folgendes verwenden:

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

Um Fehlermeldungen von openssl anzuzeigen, können Sie Folgendes verwenden: echo openssl_error_string();

Hoffentlich hilft das.

Kai Noack
quelle