verschlüsselte Dateien "vergammeln" scheinbar

Sprint

Erfahrenes Mitglied
Hallo zusammen,

wir nutzen schon seit ein paar Jahren diese (nicht selbst geschriebene) Routine, um Dateien beim Upload zu verschlüsseln und beim Download wieder zu entschlüsseln. Das hat immer wunderbar funktioniert, bis wir vor ein paar Wochen eine neue Seite installiert haben. Seitdem lassen sich immer noch viele Dateien entschlüsseln, aber nicht mehr alle. Mit der neuen Seite ist auch ein Wechsel von PHP 5.6 zu 7.4 einhergegangen, aber auch auf einer Kopie der alten Seite auf einer Subdomain mit PHP 5 brachte die selben Fehler. Auch eine Kopie der alten Seite auf einer XAMPP Emulation hängt an den selben Dateien.

Natürlich ist auf der neuen Seite fast alles auch neu geschrieben worden, aber natürlich nicht dieses Script. Der Chef meint trotzdem, es läge daran, aber warum hat es dann jahrelang funktioniert und funktioniert auch jetzt noch zu über 99%?
Der Provider meint, es läge am Content. Könnte man nachvollziehen, aber wir haben schon ein paar Mal den Fall gehabt, daß eine Datei sich auf der Live Seite entschlüsseln ließ und auf einer Test-Subdomain ebenfalls unter dem neuen Script nicht. Und auch umgekehrt.
Wir hatten jetzt neulich den Fall, daß eine Kollegin eine Datei entschlüsseln konnte, eine andere die selbe Datei eine Minute später nicht und erstere Kollegin eine Weile später auch nicht mehr.
Komischerweise sind auch Dateien, die aus einem Bereich hochgeladen werden, hundertfach mehr betroffen als Dateien, die woanders hochgeladen werden oder direkt vom System erzeugt und verschlüsselt werden. Es greifen aber alle Teile, bei denen verschlüsselte Dateien eine Rolle spielen, auf ein- und dieselbe Routine zu. Es kann also auch nicht sein, daß unterschiedliche Scripte da reinspielen könnten.

Ich habe auch schon Monate alte verschlüsselte Dateien aus einem Backup mit der aktuellen Datei verglichen. Die MD5 Checksumme ist identisch und trotzdem läßt sich auch die alte Datei nicht entschlüsseln.

Bleibt eigentlich fast nur noch eine Änderung bei den verwendeten PHP Befehlen, aber auch da konnte ich nichts finden. Inzwischen bin ich echt am Ende meiner Fähigkeiten und habe keine Idee mehr, was es noch sein könnte.

Das ist die eigentliche Ver- bzw. Entschlüsselung:
PHP:
define('FIRSTKEY','XXXXXXXXXXXXXXXX');
define('SECONDKEY','YYYYYYYYYYYYYYYYYYYYYYYYYYY');
function secured_encrypt_data($data, $k1, $k2, $verschluesselt){
    if ($k1 != '' and $k2 != ''){
        if ($verschluesselt == 1){
            $k1 = secured_decrypt_data($k1, '', '');
            $k2 = secured_decrypt_data($k2, '', '');
        }
        $first_key = base64_decode($k1);
        $second_key = base64_decode($k2);
    }else{
        $first_key = base64_decode(FIRSTKEY);
        $second_key = base64_decode(SECONDKEY);
    }

    $method = "aes-256-cbc";
    $iv_length = openssl_cipher_iv_length($method);
    $iv = openssl_random_pseudo_bytes($iv_length);

    $first_encrypted = openssl_encrypt($data,$method,$first_key, OPENSSL_RAW_DATA ,$iv);
    $second_encrypted = hash_hmac('sha512', $first_encrypted, $second_key, TRUE);

    $output = base64_encode($iv.$second_encrypted.$first_encrypted);
    return $output;
}

function secured_decrypt_data($input, $k1, $k2){
    if ($k1 != '' and $k2 != ''){
        $k1a = secured_decrypt_data($k1, '', '');
        $k2a = secured_decrypt_data($k2, '', '');
        $first_key = base64_decode($k1a);
        $second_key = base64_decode($k2a);
    }else{
        $first_key = base64_decode(FIRSTKEY);
        $second_key = base64_decode(SECONDKEY);
    }
    $mix = base64_decode($input);

    $method = "aes-256-cbc";
    $iv_length = openssl_cipher_iv_length($method);

    $iv = substr($mix,0,$iv_length);
    $second_encrypted = substr($mix,$iv_length,64);
    $first_encrypted = substr($mix,$iv_length+64);

    $data = openssl_decrypt($first_encrypted,$method,$first_key,OPENSSL_RAW_DATA,$iv);
    $second_encrypted_new = hash_hmac('sha512', $first_encrypted, $second_key, TRUE);

    if (hash_equals($second_encrypted,$second_encrypted_new))
        return $data;
}

define('FILE_ENCRYPTION_BLOCKS', 10000);

function encryptFile($source, $key, $dest){
/**
* Encrypt the passed file and saves the result in a new file with ".enc" as suffix.
*
* @param string $source Path to file that should be encrypted
* @param string $key    The key used for the encryption
* @param string $dest    File name where the encryped file should be written to.
* @return string|false    Returns the file name that has been created or FALSE if an error occured
*/
    $key = secured_decrypt_data($key, '', '');

    $key = substr(sha1($key, true), 0, 16);
    $iv = openssl_random_pseudo_bytes(16);

    $error = false;
    if ($fpOut = fopen($dest, 'x')) {
        // Put the initialzation vector to the beginning of the file
        fwrite($fpOut, $iv);
        if ($fpIn = fopen($source, 'rb')) {
            while (!feof($fpIn)) {
                $plaintext = fread($fpIn, 16 * FILE_ENCRYPTION_BLOCKS);
                $ciphertext = openssl_encrypt($plaintext, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
                // Use the first 16 bytes of the ciphertext as the next initialization vector
                $iv = substr($ciphertext, 0, 16);
                fwrite($fpOut, $ciphertext);
            }
            fclose($fpIn);
        } else {
            $error = true;
        }
        fclose($fpOut);
    } else {
        $error = true;
    }

    return $error ? false : $dest;
}

function decryptFile($source, $key, $dest){
/**
* Dencrypt the passed file and saves the result in a new file, removing the
* last 4 characters from file name.
*
* @param string $source Path to file that should be decrypted
* @param string $key    The key used for the decryption (must be the same as for encryption)
* @param string $dest    File name where the decryped file should be written to.
* @return string|false    Returns the file name that has been created or FALSE if an error occured
*/
    $key = secured_decrypt_data($key, '', '');
    $key = substr(sha1($key, true), 0, 16);

    $error = false;
    if ($fpOut = fopen($dest, 'w')) {
        if ($fpIn = fopen($source, 'rb')) {
            // Get the initialzation vector from the beginning of the file
            $iv = fread($fpIn, 16);
            while (!feof($fpIn)) {
                // we have to read one block more for decrypting than for encrypting
                $ciphertext = fread($fpIn, 16 * (FILE_ENCRYPTION_BLOCKS + 1));
                $plaintext = openssl_decrypt($ciphertext, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
                // Use the first 16 bytes of the ciphertext as the next initialization vector
                $iv = substr($ciphertext, 0, 16);
                fwrite($fpOut, $plaintext);
            }
            fclose($fpIn);
        } else {
            $error = true;
        }
        fclose($fpOut);
    } else {
        $error = true;
    }
    return $error ? false : $dest;
}

Und so werden die Dateien gehandhabt:
PHP:
function dencFile($xkdnr, $xmethode, $xdatei, $xuserid){    // Kundennummer, Ver- / Entschlüsselung, Dateiname incl. Pfad, Userid
    global $mysqli;
    if ($xkdnr != ''){
        $sql="select k2 from kunden where kdnr = '$xkdnr';";
        $erg=mysqli_query($mysqli,$sql);
        if (!$erg)
            exit ('<font color="red">Datenbankfehler: '.__FILE__.' Zeile '.__LINE__);
        $db_keys = mysqli_fetch_array($erg, MYSQLI_ASSOC);
    }else
        die();
    if (empty($db_keys))
        die();
    else
        $key2 = $db_keys['k2'];

    if (file_exists($xdatei)){
        if ($xmethode == 'e'){                                                                                                // Datei verschlüsseln
            $pathparts = pathinfo($xdatei);
            $upname = $pathparts['dirname'].'/'.strtoupper($pathparts['basename']);                                                // Dateiname in Großbuchstaben
            $zieldatei = $upname.'.enc';                                                                                    // neuen Namen festlegen
            $orgsha = sha1_file($upname);                                                                                    // SHA1 Wert Org.datei
            $verschluesselt = false;
            $versuche = 0;
            while (!$verschluesselt && $versuche < 3){                                                                        // 3 Verschlüsselungsversuche
                $enc_datei = encryptFile($xdatei, $key2, $zieldatei);                                                            // Verschlüsselung

                decryptFile($zieldatei, $key2, $_SERVER["DOCUMENT_ROOT"].'/temp/'.$xuserid.'testfile.nix');                        // Entschlüsselung in temp. Datei
                $decsha = sha1_file($_SERVER["DOCUMENT_ROOT"].'/temp/'.$xuserid.'testfile.nix');                                // SHA1 Wert entschlüsselte Datei
                if ($orgsha == $decsha){                                                                                        // wenn SHA1 Werte gleich
                    $sqlr = "select lnr from filesha where datei = '$upname'";                                                    // Datei bereits vorhanden?
                    $ergr = mysqli_query($mysqli, $sqlr);
                    if (!$ergr)
                        die('<font color="red">Datenbankfehler: '.__FILE__.' Zeile '.__LINE__);
                    $isdrin = mysqli_fetch_array($ergr, MYSQLI_ASSOC);
                    if (!empty($isdrin))
                        $sqlw = "update filesha set sha = '$orgsha' where lnr = '".$isdrin['lnr']."'";
                    else
                        $sqlw = "insert into filesha (datei, sha) values ('$upname', '$orgsha')";                                // SHA1 Wert sichern
                    $ergw = mysqli_query($mysqli, $sqlw);
                    if (!$ergw)
                        die('<font color="red">Datenbankfehler: '.__FILE__.' Zeile '.__LINE__);
                    unlink($xdatei);                                                                                            // Org.datei löschen
                    unlink($_SERVER["DOCUMENT_ROOT"].'/temp/'.$xuserid.'testfile.nix');                                            // Temp. Datei löschen
                    $verschluesselt = true;
                }else{                                                                                                            // SHA1 Wert unterschiedlich
                    unlink($zieldatei);                                                                                            // ENC Datei löschen
                    unlink($_SERVER["DOCUMENT_ROOT"].'/temp/'.$xuserid.'testfile.nix');                                            // Temp. Datei löschen
                    $versuche++;
                }
            }
            if (!$verschluesselt && $versuche == 3)                                                                            // wenn drei Verschlüsselungsversuche fehlgeschlagen
                unlink($_SERVER["DOCUMENT_ROOT"].'/temp/'.$xuserid.'testfile.nix');                                            // Temp. Datei löschen
        }elseif ($xmethode == 'd'){                                                                                            // Datei entschlüsseln
            $orgdatei = substr($xdatei,0,-4);                                                                                // org. Dateiname
            $sql="select sha from filesha where datei = '$orgdatei';";                                                        // org. SHA1 Wert auslesen
            $erg=mysqli_query($mysqli,$sql);
            if (!$erg)
                exit ('<font color="red">Datenbankfehler: '.__FILE__.' Zeile '.__LINE__);
            $orgsha = mysqli_fetch_array($erg, MYSQLI_ASSOC);
            $zieldatei = $_SERVER["DOCUMENT_ROOT"].'/temp/'.$xuserid.'-'.substr(basename($xdatei),0,-4);                    // Zieldatei festlegen
            $entschluesselt = false;
            $versuche = 0;
            while (!$entschluesselt && $versuche < 3){                                                                        // 3 Verschlüsselungsversuche
                $dec_datei = decryptFile($xdatei, $key2, $zieldatei);
                $decsha = sha1_file($zieldatei);
                if ($decsha == $orgsha['sha']){
                    $entschluesselt = true;

                    if(file_exists($zieldatei)) {
                        if ($fd = fopen ($zieldatei, "r")) {
                            $fsize = filesize($zieldatei);
                            header("Content-type: application/octet-stream");
                            header('Content-Disposition: attachment; filename="'.substr(basename($xdatei),0,-4).'"');
                            header("Content-length: $fsize");
                            while(!feof($fd)) {
                                $buffer = fread($fd, 2048);
                                echo $buffer;
                            }
                        }
                        fclose ($fd);
                        unlink($zieldatei);
                        exit;
                    }
                }else{
                    $versuche++;
                    unlink($zieldatei);                                                                                        // ENC Datei löschen
                }
            }
            if (!$entschluesselt && $versuche == 3)                                                                            // wenn drei Verschlüsselungsversuche fehlgeschlagen
                unlink($zieldatei);                                                                                            // Temp. Datei löschen
        }
    }
}

Und aufgerufen wird das so:
PHP:
dencFile(12345, 'd', 'pfad/datei.php.enc', 651649846);
//dencFile($kundenummer, $methode, $quelldatei, $rand);
//die Zufallszahl ist für den Fall, daß zwei User auf die selbe Datei zugreifen

Wären es einfach nur irgendwelche Dateien, wäre der Verlust nicht so schlimm, aber so kann eine nicht mehr entschlüsselbare Datei RICHTIG Geld kosten. Ich hoffe, daß jemand wenigstens eine Idee hat, woran es liegen könnte.
 
Zurück