26

I am searching for a way to encrypt a .txt file into a zip, but in a secure password protected way. My goal is to email this file to me, without anyone being able to read the content of the attachment.

Does anybody know an easy, and above all, secure way to accomplish this ? I can create zip archives, but I do not know how to encrypt them, or, how secure this is.

Dan McClain
  • 11,780
  • 9
  • 47
  • 67
Digits
  • 1,311
  • 2
  • 12
  • 21

5 Answers5

23

As of php 7.2 (which was released a hours ago), the right way to do this is to use additional functionality included in ZipArchive native php code. (thanks to abraham-tugalov for pointing out that this change was coming)

Now the simple answer looks something like this:

<?php
$zip = new ZipArchive();
if ($zip->open('test.zip', ZipArchive::CREATE) === TRUE) {
    $zip->setPassword('secret_used_as_default_for_all_files'); //set default password

    $zip->addFile('thing1.txt'); //add file
    $zip->setEncryptionName('thing1.txt', ZipArchive::EM_AES_256); //encrypt it

    $zip->addFile('thing2.txt'); //add file
    $zip->setEncryptionName('thing2.txt', ZipArchive::EM_AES_256); //encrypt it

    $zip->close();

    echo "Added thing1 and thing2 with the same password\n";
} else {
    echo "KO\n";
}
?>

But you can also set the encryption method by index and not name, and you can set each password on a per-file basis... as well as specify weaker encryption options, using the newly supported encryption options.

This example exercises these more complex options.

<?php
$zip = new ZipArchive();
if ($zip->open('test.zip', ZipArchive::CREATE) === TRUE) { 
     //being here means that we were able to create the file..

     //setting this means that we do not need to pass in a password to every file, this will be the default
    $zip->addFile('thing3.txt');

    //$zip->setEncryptionName('thing3.txt', ZipArchive::EM_AES_128);
    //$zip->setEncryptionName('thing3.txt', ZipArchive::EM_AES_192);
    //you should just use ZipArchive::EM_AES_256 unless you have super-good reason why not. 
    $zip->setEncryptionName('thing3.txt', ZipArchive::EM_AES_256, 'password_for_thing3');

     $zip->addFile('thing4.txt');
    //or you can also use the index (starting at 0) of the file...
    //which means the following line should do the same thing...
    //but just referencing the text.txt by index instead of name..
    //$zip->setEncryptionIndex(1, ZipArchive::EM_AES_256, 'password_for_thing_4'); //encrypt thing4, using its index instead of its name...

    $zip->close();
    echo "Added thing3 and thing4 with two different passwords\n";
} else {
    echo "KO\n";
}
?>

The underlying support for zip encryption is enabled because libzip 1.2.0 introduced support for encryption. So you will need to have php 7.2 and libzip 7.2 in order to run this code... Hopefully this note will be cruft on this answer "real soon"

ftrotter
  • 3,066
  • 2
  • 38
  • 52
  • 4
    Wow, this actually works. Very counter-intuitive that you have to set the encryption theme for each file individually. – nights Mar 26 '20 at 03:09
21

Note: this answer recommends a cryptographic method that is known insecure, even with good password. Please see link from comments and the Winzip QA on AES. Support for in-php AES zip encryption arrives with php 7.2 (and libzip 1.2.0), which means this answer will soon be outdated too. Until then see this answer for how to call out to 7z instead of the zip command, which supports winzip's AES encryption.

You can use this:

<?php echo system('zip -P pass file.zip file.txt'); ?>

Where pass is the password, and file.txt will be zipped into file.zip. This should work on Windows and Linux, you just need to get a free version of zip for Windows ( http://www.info-zip.org/Zip.html#Win32 )

This kind of security can be broken by brute force attacks, dictionary attacks and etc. But it's not that easy, specially if you chose a long and hard to guess password.

ftrotter
  • 3,066
  • 2
  • 38
  • 52
lbrandao
  • 4,144
  • 4
  • 35
  • 43
  • thank you, I will try this. it is on a linuxserver I'm working, so the add-on will not be necessary. – Digits Mar 14 '09 at 17:11
  • 4
    ZIP encryption is actually pretty weak, there are attacks that yield a working password (if not necessarily the same password that was originally used) in relatively short time. – bobince Mar 14 '09 at 17:41
  • 4
    This answer is actively dangerous. The encryption used by this answer is horribly weak. It is *not* the modern AES-based encryption. – Jon Bright Mar 08 '12 at 11:26
  • 1
    Many hosting environments block the "system" function. Using the ZipArchive class is better. – Maciej Swic Dec 23 '12 at 11:23
  • Not sure what kind of security is needed. But do an encryption first and then mail yourself a zipped file might be a better route. Or even mail yourself a link and create the file on demand on accessing that link. – Joeri Jan 01 '14 at 21:22
  • Note that with system("..."), there is no need to use echo, see "https://stackoverflow.com/a/6708202/6463291" – Ng Sek Long Oct 15 '18 at 10:03
  • Since PHP 7.2 this is no longer the correct answer. setEncryptionName() is now the correct method. – Cagy79 May 27 '19 at 11:53
4

Although PHP is a mature language, there is no adequate method (excluding custom extension or something like that) to achieve such a simple task with pure PHP.

What you also can do, is to wait until PHP 7.2 will be available for production (because ZipArchive::setEncryptionName is implemented (thanks to Pierre and Remi)).

But, until then you also can try to port php_zip >= 1.14.0 to PHP < 7.2, but there is currently no compiled binaries available, so you have to compile it yourself and try if it is possible at all (I believe it is).

PS I would try it, but have no VS2015+ on my PC right now.

halfer
  • 19,824
  • 17
  • 99
  • 186
Abraham Tugalov
  • 1,902
  • 18
  • 25
0

More and more tools are supporting AES-encrypted ZIP files. It works, it's secure.

EDIT2: You can use DotNetZip from PHP to dynamically generate AES-encrypted zip archives from PHP. DotNetZip is a .NET library that is designed for .NET languages (C#, VB, etc). It runs only on Windows :(. But DotNetZip does AES, and it's free, and it works from PHP.

This is the code I used. (PHP v5.2.9 on Win32)

<?php
try
{
  $fname = "zip-generated-from-php-" . date('Y-m-d-His') . ".zip";
  $zipOutput = "c:\\temp\\" . $fname;
  $zipfact = new COM("Ionic.Zip.ZipFile");
  $zip->Name = $zipOutput;
  $dirToZip= "c:\\temp\\psh";
  # Encryption:  3 => 256-bit AES.  
  #     2 => 128-bit AES.  
  #     1 => PKZIP (Weak).  
  #     0 => None
  $zip->Encryption = 3;
  $zip->Password = "AES-Encryption-Is-Secure";
  $zip->AddDirectory($dirToZip);
  $zip->Save();
  $zip->Dispose();

  if (file_exists($zipOutput))
  {
    header('Cache-Control: no-cache, must-revalidate');
    header('Content-Type: application/x-zip'); 
    header('Content-Disposition: attachment; filename=' . $fname);
    header('Content-Length: ' . filesize($zipOutput));
    readfile($zipOutput);
    unlink($zipOutput);
  }
  else 
  {
    echo '<html>';
    echo '  <head>';
    echo '  <title>Calling DotNetZip from PHP through COM</title>';
    echo '  <link rel="stylesheet" href="basic.css"/>';
    echo '  </head>';
    echo '<body>';
    echo '<h2>Whoops!</h2>' . "<br/>\n";
    echo '<p>The file was not successfully generated.</p>';
    echo '</body>';
    echo '</html>';
  } 
}
catch (Exception $e) 
{
    echo '<html>';
    echo '  <head>';
    echo '  <title>Calling DotNetZip from PHP through COM</title>';
    echo '  <link rel="stylesheet" href="basic.css"/>';
    echo '  </head>';
    echo '<body>';
    echo '<h2>Whoops!</h2>' . "<br/>\n";
    echo '<p>The file was not successfully generated.</p>';
    echo '<p>Caught exception: ',  $e->getMessage(), '</p>', "\n";
    echo '<pre>';
    echo $e->getTraceAsString(), "\n";
    echo '</pre>';
    echo '</body>';
    echo '</html>';
}

?>

I had to modify DotNetZip to make it work with PHP: I had to make the Name property read/write, and I had to make it COM-callable. This change is first available in the v1.8.2.3 release.

Cheeso
  • 189,189
  • 101
  • 473
  • 713
  • and do you know how i can use this in a php script ? many thanks! – Digits Mar 27 '09 at 11:09
  • 1
    if you run PHP on Windows, there's DotNetZip (http://dotnetzip.codeplex.com) that supports AES-encrypted zips. PHP can invoke .NET components. badda bing, badda boom. – Cheeso Mar 27 '09 at 15:49
  • 1
    unfortunately most of my servers run on linux. but i can use this solution for the ones on windows. thank you very much! – Digits Apr 13 '09 at 10:09
  • I'd go with encrypting the data with gibberish-aes-php. Zip that ( or not ), download, and run a local cli-php-script to decrypt. There is also a javascript implementation of gibberish-aes so the clientside could also be javascript. ( https://github.com/ivantcholakov/gibberish-aes-php ) – Joeri Jan 01 '14 at 21:23
  • This is a great answer. But it should get notched down now that support is in php 7.2... – ftrotter Dec 01 '17 at 08:08
-4

This is how I did it. It's with an excel, but it's the same thing.

  • Let php create a random codename.
  • Save the codename in db or in a file to be included by the retrieve.php.
  • Mail yourself the codename.

  • Access via web the retrieve.php?codename=[codename]

  • Let php check if codename is correct. (maybe even it's age).
  • Create the excel/textfile in memory from the data that needs to be send to you.
  • Create an encryption code by adding a local password (that only you know) with the codename.( I say add but can also be mixed or xor-ed or substr... )
  • Encrypt on the fly with the created encryption code.
  • Remove the codename from db or config file.
  • Return this encrypted document in a mail (zipped or not for size).
  • Maybe add two first characters from codename in the mail to know what local full codename to use.

  • Use a local decryption script to decode the downloaded file. Use same codename/password algorithm to create a decryption key.

I use gibberish-aes-php. ( https://github.com/ivantcholakov/gibberish-aes-php )
Because then I can use https://github.com/mdp/gibberish-aes as javascript on a client decoder (for things I want to take a quick peek at in a browser).

Joeri
  • 2,214
  • 24
  • 24