1

I have for a while been using the excellent Defuse PHP Encryption library for storing encrypted data strings in a database.

I have an extension of this need where I now have files that need to be uploaded and secured at rest. these files are typically no larger than 500Kb documents.

I have been looking into the Defuse / PHP Encryption File class and the system appears to work perfectly for saving newly uploaded files.

However, I'm exploring the best method for opening these encrypted files. The Defuse\Crypto\File class obviously can decrypt the files but it seems to only be able to decrypt them to another, plaintext file.

Is it possible to decrypt the file and output to a string only?

If for instance someone opens an encrypted file, it is decrypted then for as long as they're on that page (the file contents would be viewed/downloaded via a PHP intermediary page so the file location itself is never presented to the user) the plaintext file would be sitting on the server and potentially accessible to anyone who can reach it.

With this in mind I have some queries:

  • Is there any possibility that - Using Defuse PHP Encryption - an encrypted file can be decrypted to a (PHP) stream / string so avoid a plaintext file sitting on the server for any amount of time?

  • I have (just) considered an approach of saving a plaintext file to the server, then using file_get_contents and then unlinking the plaintext file. (This is my preferred approach at the moment but I have only thought of it while writing this question)

    • On Linux servers I believe it would be preferential to overwrite the filedata such as with random_bytes before unlinking the plaintext file. Is this correct?

      My thoughs on this come from unlink only deletes the link to the file and so (potentially) the file may still exist in practise (albeit harder to reach) until the local disk does its usual cleaning up.

  • Is there any advantage -as a concept- to instead loading the plaintext file data from the upload and encrypting that data string with Defuse\Crypto\Crypto and then saving this Crypto encrypted data blob into the storage file?

Research links

I've read the github for the library, and also read the issues and comments surrounding decrypting files to streams but these issues/comments faced more about excessive file sizes, (and the fact the whole file needs to be read). These won't apply in my case.

I have also read a top post from the PHP unlink manual page which seemed to confirm my thoughts on unlink.

4
  • With a log structured filesystem, even shredding the file may leave traces of the plaintext on the server. If you need tin foil hat security, encrypt and decrypt on the client.
    – symcbean
    Jul 22, 2016 at 19:06
  • 1
    Uploading leaves an unencrypted footprint (albeit as a deleted file if you are doing your job properly). I assume you have some magic mojo for accessing the key without storing it on the filesystem. I suggest you implement as a streamwrapper - php.net/manual/en/class.streamwrapper.php - or invoke an external command to decrypt to stdout and read from there (again the read might be implemented as a streamwrapper). Remember to make the seek method fail.
    – symcbean
    Dec 21, 2016 at 1:25
  • Thanks for the info, @symcbean , and the answer by Kyrth is useful too. I did construct a solution to this but it's been a couple of months since I've looked into this topic so I'm wary of saying much without checking what I actually did (my memory is not great). I think I created a wrapper.
    – Martin
    Dec 21, 2016 at 16:19
  • I have precisely this need, but I do need to support excessive file sizes, so reading the entire file into memory is not an option. It seems like the implementation would need to read a chunk into a file pointer, decrypt it, and write the plaintext chunk to a separate stream, which is sent to the client via fpassthru($stream) or similar (rinse and repeat). If this is even feasible and sane, I'm struggling to conceptualize how this could possibly work without modifying the Defuse library code. Any guidance would be fantastic. Jan 6, 2020 at 16:51

1 Answer 1

1

I tested out encrypting with the File class, then reading the file contents and decrypting with Crypto::decrypt(), however they are not compatible out of the box.

So I ended up writing a small wrapper class in the vein of your third idea -- read the plain file, encrypt the string, and write the encrypted data to disk. It includes this function (note that $this->key() is loaded/unlocked elsewhere in the class):

use Defuse\Crypto\Exception as Ex;
use Defuse\Crypto\Crypto;

public function encrypt_file($source, $destination, $password = "") {
    try {
        if(is_file($source) === true) {
            $string = file_get_contents($source);

            if($password == "") {                   // Key object-based encryption
                if(file_put_contents($destination, Crypto::encrypt($string, $this->key), LOCK_EX) !== false)
                    return true;
            }
            else {                                  // password-based encryption
                if(file_put_contents($destination, Crypto::encryptWithPassword($string, $password), LOCK_EX) !== false)
                    return true;
            }
        }

        return false; 
    }
    catch(Ex\EnvironmentIsBrokenException $e) {
        return false;
    }
}

Now I can read it with a file_get_centents() and a Crypto::decrypt(), and it's easy to dump the plain file back to disk if I really want to (which I generaly don't).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .