Storing PHP(/PHP-FPM/Apache)'s temporary-from-upload files in RAM rather than the filesystem (or encrypted only)? Storing PHP(/PHP-FPM/Apache)'s temporary-from-upload files in RAM rather than the filesystem (or encrypted only)? apache apache

Storing PHP(/PHP-FPM/Apache)'s temporary-from-upload files in RAM rather than the filesystem (or encrypted only)?


Have you considered putting a layer between the user and the web server? Using something like perlbal with some custom code in front of the web server would allow you to intercept uploaded files before they are written anywhere, encrypt them, write them to a local ramdisk and then proxy the request on the the web server proper (with the filename and decryption key to the files).

If the PHP process crashes, the encrypted file is left around but can't be decrypted. No unencrypted data gets written to (ram)disk.


CGI to the rescue!

If you create a cgi-bin directory, and configure appropriately, you'll get the message via stdin (as far as I can tell, files aren't written to disk at all this way).

So, in your apache config add

ScriptAlias /cgi-bin/ /var/www/<site-dir>/cgi-bin/<Directory "/var/www/<site-dir>/cgi-bin">    AllowOverride None    Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch    Order allow,deny    Allow from all</Directory>

Then write a CGI-mode PHP script to parse the post data. From my (limited) testing, no local files appear to be created. The sample dumps what it reads from stdin as well as the environment variables, to give you an idea of what's there to work with.

Sample script installed as /var/www//cgi-bin/test

#!/usr/bin/phpContent-type: text/html<html><body><form enctype="multipart/form-data" action="/cgi-bin/test" method="POST">    <!-- MAX_FILE_SIZE must precede the file input field -->    <input type="hidden" name="MAX_FILE_SIZE" value="30000" />    <!-- Name of input element determines name in $_FILES array -->    Send this file: <input name="userfile" type="file" />      <input type="submit" value="Send File" /></form><pre><?echo "\nRequest body\n\n";$handle = fopen ("php://stdin","r");while (($line = fgets($handle))) echo "$line";fclose($handle);echo "\n\n";phpinfo(INFO_ENVIRONMENT);echo "\n\n";?></pre></body></html>

Sample outputThis is the output (source) when I upload a plain-text file:

<html><body><form enctype="multipart/form-data" action="/cgi-bin/test" method="POST">    <!-- MAX_FILE_SIZE must precede the file input field -->        <input type="hidden" name="MAX_FILE_SIZE" value="30000" />        <!-- Name of input element determines name in $_FILES array -->        Send this file: <input name="userfile" type="file" />          <input type="submit" value="Send File" /></form><pre>Request body-----------------------------19908123511077915841334811274Content-Disposition: form-data; name="MAX_FILE_SIZE"30000-----------------------------19908123511077915841334811274Content-Disposition: form-data; name="userfile"; filename="uploadtest.txt"Content-Type: text/plainThis is some sample text-----------------------------19908123511077915841334811274--phpinfo()EnvironmentVariable => ValueHTTP_HOST => dev.squello.comHTTP_USER_AGENT => Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.9.2.16) Gecko/20110323 Ubuntu/10.04 (lucid) Firefox/3.6.16HTTP_ACCEPT => text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8HTTP_ACCEPT_LANGUAGE => en-gb,en;q=0.5HTTP_ACCEPT_ENCODING => gzip,deflateHTTP_ACCEPT_CHARSET => ISO-8859-1,utf-8;q=0.7,*;q=0.7HTTP_KEEP_ALIVE => 115HTTP_CONNECTION => keep-aliveHTTP_REFERER => http://dev.squello.com/cgi-bin/testCONTENT_TYPE => multipart/form-data; boundary=---------------------------19908123511077915841334811274CONTENT_LENGTH => 376PATH => /usr/local/bin:/usr/bin:/binSERVER_SIGNATURE => <address>Apache/2.2.14 (Ubuntu) Server at dev.squello.com Port 80</address>SERVER_SOFTWARE => Apache/2.2.14 (Ubuntu)SERVER_NAME => dev.squello.comSERVER_ADDR => 127.0.0.1SERVER_PORT => 80REMOTE_ADDR => 127.0.0.1DOCUMENT_ROOT => /var/www/dev.squello.com/wwwSERVER_ADMIN => webmaster@localhostSCRIPT_FILENAME => /var/www/dev.squello.com/cgi-bin/testREMOTE_PORT => 58012GATEWAY_INTERFACE => CGI/1.1SERVER_PROTOCOL => HTTP/1.1REQUEST_METHOD => POSTQUERY_STRING =>  REQUEST_URI => /cgi-bin/testSCRIPT_NAME => /cgi-bin/test</pre></body></html>


I had a flash of inspiration on this: black-hole filesystems.

Essentially, this is a fake filesystem, where data never gets written, but all files exists, and have no content.

There's a discussion on unix.se about these, and one answer involves a FUSE implementation of just this (quoted here):

This isn't supported out-of-the-box on any unix I know, but you can do pretty much anything with FUSE. There's at least one implementation of nullfs¹, a filesystem where every file exists and behaves like /dev/null (this isn't the only implementation I've ever seen).

¹ Not to be confused with the *BSD nullfs, which is analogous to bindfs.

I haven't had a chance to test this but if you set the upload_tmp_dir to a black-hole location, the upload (would|should) never be written to disk, but still be available in $HTTP_RAW_POST_DATA (or php://input). If it works, it's better than patching PHP