Frage Speichern von temporären Upload-Dateien von PHP (/ PHP-FPM / Apache) im RAM anstatt im Dateisystem (oder nur verschlüsselt)?


Ursprüngliche Frage

Das Projekt, an dem ich gerade arbeite, ist bei Datei-Uploads geradezu paranoid.
Im Rahmen dieser Frage verwende ich diesen Begriff nicht in Bezug auf Nutzlasten; Ich rede Vertraulichkeit.

Programme können immer abstürzen und temporäre Dateien im Dateisystem herumhängen lassen. Das ist normal. Das leicht Vertraulichkeit-Paranoid kann einen Cronjob schreiben, der alle paar Minuten den temporären Dateiordner anspricht und alles löscht, das älter als ein paar Sekunden vor dem Cronjob-Aufruf ist (nicht alles, einfach weil sonst eine Datei beim Hochladen auffängt).

... leider nehmen wir diesen Paranoiden einen Schritt weiter:

Im Idealfall würden wir niemals temporäre Dateien von Datei-Uploads sehen, sondern nur im prozessbezogenen RAM.

Gibt es eine Möglichkeit, PHP beizubringen, nach temporären Dateien als Blobs im Speicher und nicht im Dateisystem zu suchen? Wir verwenden PHP-FPM als CGI-Handler und Apache als unseren Webserver, falls es das leichter macht. (Beachten Sie auch: 'Dateisystem' ist hier das Schlüsselwort, nicht 'Disk', da es natürlich Möglichkeiten gibt, das Dateisystem dem RAM zuzuordnen, aber das behebt nicht die Problematik der Zugänglichkeit und des automatischen Absturzes nach dem Absturz. )

Alternativ gibt es eine Möglichkeit, wie diese temporären Dateien sein können verschlüsselt sofort, wenn sie auf Disc geschrieben werden, so dass sie niemals im Dateisystem ohne Verschlüsselung gehalten werden?


Thread Übersicht

Ich kann leider nur akzeptieren ein Antwort - aber für jeden, der dies liest, ist der ganze Thread äußerst wertvoll und enthält die kollektiven Einsichten vieler Menschen. Abhängig von was Sie hoffen, die akzeptierte Antwort zu erreichen möglicherweise nicht interessant für dich. Wenn Sie über eine Suchmaschine hierher gekommen sind, Bitte nehmen Sie sich einen Moment Zeit, um den ganzen Thread zu lesen.

Hier ist eine Zusammenstellung von Use Cases, wie ich sie zur schnellen Referenz sehe:

Re: PHP's temporäre Dateien

  • RAM statt Disc (z. B. aufgrund von E / A-Problemen) → RAMdisk/vergleichbar (Plasmid87, Joe Hopfgartner)

  • Sofortige Verschlüsselung (pro Dateisystem-Benutzer) → encFS (ADW) (+ eine Frage nach Sander Marechal)

  • Sichere Dateiberechtigungen → restriktive native Linux Berechtigungen (optional per vhost) (Gilles) oder SELinux (siehe verschiedene Kommentare)

  • Prozessgebundener Speicher anstelle von Dateisystem (so dass ein Prozessabsturz die Dateien entfernt) (ursprünglich durch die Frage gemeint)

    • Lass die Dateidaten nicht direkt in PHP ankommen → Reverse-Proxy (Cal)

    • Deaktivieren Sie das Schreiben von PHP in das Dateisystem → Siehe PHP Bug Link in dieser Antwort (Stephan B) oder Führen Sie PHP im CGI-Modus aus (Phil Lello)

    • schreibgeschützte Dateien → /dev/null Dateisystem (Phil Lello) (Dies ist nützlich, wenn Sie Zugriff auf die Daten als Stream haben zusätzlich Die Funktion zum Schreiben von Dateien, die parallel ausgeführt wird, kann jedoch nicht deaktiviert werden. ob PHP dies erlaubt ist unklar)

Re: Ihre Dateien, Post-Upload


38
2018-04-18 10:25


Ursprung


Antworten:


Haben Sie darüber nachgedacht, eine Ebene zwischen Benutzer und Webserver zu setzen? Mit etwas wie perlbal Mit etwas benutzerdefiniertem Code vor dem Webserver können Sie hochgeladene Dateien abfangen, bevor sie irgendwo geschrieben werden, sie verschlüsseln, sie auf eine lokale Ramdisk schreiben und dann die Anfrage auf dem eigentlichen Webserver (mit dem Dateinamen und dem Entschlüsselungsschlüssel) übernehmen zu den Dateien).

Wenn der PHP-Prozess abstürzt, bleibt die verschlüsselte Datei erhalten, kann aber nicht entschlüsselt werden. Es werden keine unverschlüsselten Daten auf die (RAM-) Platte geschrieben.


5
2018-05-04 03:43



CGI zur Rettung!

Wenn Sie ein cgi-bin-Verzeichnis erstellen und entsprechend konfigurieren, erhalten Sie die Nachricht über stdin (soweit ich feststellen kann, dass Dateien nicht auf diese Weise auf die Festplatte geschrieben werden).

Also, in Ihrer Apache Config hinzufügen

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>

Schreiben Sie dann ein PHP-Skript im CGI-Modus, um die Postdaten zu analysieren. Von meinen (begrenzten) Tests scheinen keine lokalen Dateien erstellt worden zu sein. Das Beispiel speichert das, was es von stdin liest, sowie die Umgebungsvariablen, um Ihnen eine Vorstellung davon zu geben, mit was es arbeiten soll.

Beispielskript installiert als / var / www // cgi-bin / test

#!/usr/bin/php
Content-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>

Beispielausgabe Dies ist die Ausgabe (Quelle), wenn ich eine Nur-Text-Datei hochlade:

<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

-----------------------------19908123511077915841334811274
Content-Disposition: form-data; name="MAX_FILE_SIZE"

30000
-----------------------------19908123511077915841334811274
Content-Disposition: form-data; name="userfile"; filename="uploadtest.txt"
Content-Type: text/plain

This is some sample text

-----------------------------19908123511077915841334811274--


phpinfo()

Environment

Variable => Value
HTTP_HOST => dev.squello.com
HTTP_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.16
HTTP_ACCEPT => text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
HTTP_ACCEPT_LANGUAGE => en-gb,en;q=0.5
HTTP_ACCEPT_ENCODING => gzip,deflate
HTTP_ACCEPT_CHARSET => ISO-8859-1,utf-8;q=0.7,*;q=0.7
HTTP_KEEP_ALIVE => 115
HTTP_CONNECTION => keep-alive
HTTP_REFERER => http://dev.squello.com/cgi-bin/test
CONTENT_TYPE => multipart/form-data; boundary=---------------------------19908123511077915841334811274
CONTENT_LENGTH => 376
PATH => /usr/local/bin:/usr/bin:/bin
SERVER_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.com
SERVER_ADDR => 127.0.0.1
SERVER_PORT => 80
REMOTE_ADDR => 127.0.0.1
DOCUMENT_ROOT => /var/www/dev.squello.com/www
SERVER_ADMIN => webmaster@localhost
SCRIPT_FILENAME => /var/www/dev.squello.com/cgi-bin/test
REMOTE_PORT => 58012
GATEWAY_INTERFACE => CGI/1.1
SERVER_PROTOCOL => HTTP/1.1
REQUEST_METHOD => POST
QUERY_STRING =>  
REQUEST_URI => /cgi-bin/test
SCRIPT_NAME => /cgi-bin/test


</pre>
</body></html>

4
2018-05-04 00:52



Ich hatte eine kleine Inspiration: Black-Hole-Dateisysteme.

Im Wesentlichen ist dies ein falsches Dateisystem, in dem Daten niemals geschrieben werden, aber alle Dateien existieren und keinen Inhalt haben.

Da ist ein Diskussion über unix.se über diese und eine Antwort beinhaltet eine FUSE-Implementierung von genau diesem (hier zitiert):

Dies wird nicht standardmäßig sofort unterstützt   jedes Unix, das ich kenne, aber du kannst es schön machen   viel mit allem    SICHERUNG.   Es gibt zumindest eine Implementierung   von   nullfs¹,   ein Dateisystem, in dem jede Datei existiert   und verhält sich wie /dev/null (Dies   ist nicht die einzige Implementierung, die ich habe   jemals gesehen).

¹  Nicht mit dem zu verwechseln    * BSD   nullfs,   das ist analog zu    bindfs.   

Ich hatte keine Chance das zu testen  aber Wenn Sie das upload_tmp_dir auf ein schwarzes Loch setzen, wird der Upload (würde | sollte) niemals auf die Festplatte geschrieben, aber in der Datei verfügbar sein $ HTTP_RAW_POST_DATA (oder php: // Eingabe). Wenn es funktioniert, ist es besser als PHP zu patchen


4
2018-05-05 22:00



Ich bin mit PHP nicht vertraut, daher wird meine Antwort nicht direkt in eine Anleitung eingeordnet, aber ich denke, Sie haben einige Missverständnisse darüber, welchen Schutz verschiedene Systemfunktionen bieten, was dazu geführt hat, dass Sie gültige Lösungen abgelehnt haben von Lösungen, die genau die gleichen Sicherheitseigenschaften haben. Aus deinen Kommentaren entnehme ich, dass du Linux betreibst; Die meisten meiner Antworten gelten für andere Unices, nicht jedoch für andere Systeme wie Windows.

Soweit ich sehen kann, sind Sie besorgt über drei Angriffsszenarien:

  1. Der Angreifer erhält physischen Zugriff auf die Maschine, schaltet ihn ab, zieht die Festplatte heraus und liest ihren Inhalt zu ihrer Freizeit vor. (Wenn der Angreifer Ihr RAM lesen kann, haben Sie bereits verloren.)
  2. Der Angreifer kann Code als Benutzer auf dem Computer ausführen.
  3. Ein Fehler in den CGI-Skripten ermöglicht es einem Prozess, temporäre Dateien zu lesen, die von anderen Prozessen erstellt wurden.

Die erste Art von Angreifer kann alles lesen, was auf der Festplatte unverschlüsselt ist, und nichts, das verschlüsselt ist mit einem Schlüssel hat sie nicht.

Was die zweite Art von Angreifer tun kann, hängt davon ab, ob sie Code als denselben Benutzer ausführen kann, der Ihre CGI-Skripts ausführt.

Wenn sie Code nur als andere Benutzer ausführen kann, dann ist das Werkzeug zum Schutz der Dateien Berechtigungen. Sie sollten ein Verzeichnis mit dem Modus 700 (= drwx------), d. h. nur für einen Benutzer zugänglich und dem Benutzer gehört, der die CGI-Skripte ausführt. Andere Benutzer können nicht auf die Dateien in diesem Verzeichnis zugreifen. Sie benötigen keine zusätzliche Verschlüsselung oder anderen Schutz.

Wenn sie Code als CGI-Benutzer ausführen kann (der natürlich Code als root ausführt), haben Sie bereits verloren. Sie können den Speicher eines anderen Prozesses sehen, wenn Sie Code als denselben Benutzer ausführen - Debugger tun es die ganze Zeit! Unter Linux können Sie es leicht für sich selbst sehen erkunden /proc/$pid/mem. Verglichen mit dem Lesen einer Datei ist das Lesen eines Prozessspeichers technisch etwas schwieriger, aber in Bezug auf die Sicherheit gibt es keinen Unterschied.

So Die Daten in Dateien sind an sich kein Sicherheitsproblem.

Betrachten wir nun die dritte Sorge. Die Sorge ist, dass ein Bug im CGI dem Angreifer erlaubt Schnüffel auf Dateien, aber keinen beliebigen Code ausführen. Dies hängt mit einem Zuverlässigkeitsproblem zusammen - wenn der CGI-Prozess abbricht, kann er temporäre Dateien zurücklassen. Aber es ist allgemeiner: Die Datei könnte von einem gleichzeitig laufenden Skript gelesen werden.

Der beste Schutz dagegen besteht darin, die Daten nicht in einer Datei zu speichern. Dies sollte auf der Ebene von PHP oder seinen Bibliotheken geschehen, und ich kann nicht anders. Wenn es nicht möglich ist, dann nullfs wie vorgeschlagen von Phil Lello ist eine vernünftige Problemumgehung: Der PHP-Prozess wird denken, dass er Daten in eine Datei schreibt, aber die Datei wird niemals wirklich Daten enthalten.

Es gibt einen weiteren häufigen Unix-Trick, der hier nützlich sein könnte: Sobald Sie eine Datei erstellt haben, können Sie Verknüpfung aufheben (entfernen) und arbeiten weiter damit. Sobald die Verknüpfung aufgehoben ist, kann auf die Datei nicht mehr mit ihrem früheren Namen zugegriffen werden, aber die Daten verbleiben im Dateisystem, solange die Datei in mindestens einem Prozess geöffnet ist. Dies ist jedoch hauptsächlich nützlich für die Zuverlässigkeit, um das Betriebssystem dazu zu bringen, die Daten zu entfernen, wenn der Prozess aus irgendeinem Grund abstürzt. Ein Angreifer, der beliebige Dateien mit den Berechtigungen des Prozesses öffnen kann, kann auf die Daten zugreifen /proc/$pid/fd/$fd. Und ein Angreifer, der Dateien jederzeit öffnen kann, hat ein kleines Fenster zwischen dem Erstellen der Datei und dem Aufheben der Verknüpfung: Wenn sie die Datei dann öffnen kann, kann sie Daten sehen, die später hinzugefügt werden. Dies kann nichtsdestoweniger ein nützlicher Schutz sein, da es den Angriff in einen zeitsensitiven schaltet und viele gleichzeitige Verbindungen erfordert, was durch einen Verbindungsratenbegrenzer konterkariert oder zumindest viel schwieriger gemacht werden könnte.


3
2018-05-05 23:30



Haben Sie sich mit FUSE beschäftigt, um ein verschlüsseltes Verzeichnis zu erstellen, auf das nur ein bestimmter Benutzer zugreifen kann?

http://www.arg0.net/encfs

Der Speicher wird nicht mit einem bestimmten Prozess verknüpft, aber die Dateien sind nur für einen bestimmten Benutzer zugänglich (derselbe, auf dem Ihr Webserver läuft, um nützlich zu sein!), Was ausreichen könnte?


1
2018-04-20 16:03



PHP speichert hochgeladene Dateien im Dateisystem, bevor Ihr Skript die Daten php: // Eingabe abfangen kann und $ HTTP_RAW_POST_DATA ist in diesem Fall leer (selbst wenn Sie es gesetzt haben) file_uploads = Off).

Für kleine Dateien könnten Sie versuchen, zu setzen <form enctype="application/x-www-form-urlencoded" ... aber das ist mir nicht gelungen. Ich schlage vor, dass Sie php neu kompilieren und den Teil, der Datei-Uploads behandelt, kommentieren Fehlerbericht (Kommentar von pollita@php.net).

Pro: Keine Datei-Uploads überhaupt, Daten in php: // Eingabe Con: Kompilieren, keine Herstellerunterstützung


1
2018-05-03 09:40



Ihre Bedenken sind gültig. Es gibt einige Lösungen für dieses Problem. Eine besteht darin, die Datei in einer Datenbank zu speichern. Eine NoSQL-Datenbank wie MongoDB oder CouchDB wurde entwickelt, um Dateien effizient zu speichern. MySQL ist eine weitere Option und hat Vorteile gegenüber NoSQL. Eine relationale Datenbank wie MySQL macht es sehr einfach, Zugriffskontrolle zu implementieren, weil Sie die Beziehung beziehen können files und users Tabelle durch einen Primärschlüssel.

In MySQL können Sie die longblob Datentyp hält 2 ^ 32 Bits oder ungefähr ~ 500mb. Mit der MEMORY-Engine können Sie eine residente Tabelle erstellen: CREATE TABLE files ENGINE=MEMORY .... Außerdem. MySQL hat Verschlüsselung in Form von aes_encrypt() und des_encrypt() aber sie benutzen beide ECB-Modus, der Müll ist.

$sensi_file=file_get_contents($_FILES['sensitive']['tmp_name']);
unlink($_FILES['sensitive']['tmp_name']);//delete the sensitive file. 
$sensi_file=mysql_real_escape_string($sensi_file);//Parametrized quires will also use this function so that should also be binary safe.
mysql_query("insert into files (file)values('$sensi_file')"); 

Wählen Sie einfach die Datei aus und verwenden Sie sie genau so, wie Sie es tun würden $sensi_file. Denken Sie daran, dass Sie die Eingabe verlassen, um die Zeichenliterale zu erhalten und somit die rohe Binärdatei zu speichern.


0
2018-05-02 20:20



Sie können ein erstellen tmpfs und mount es mit einer richtigen umask. Auf diese Weise sind die einzigen Prozesse, die Dateien davon lesen können, die Benutzer, die sie erstellt haben. Und weil das a ist tmpfsNichts wird jemals auf der Festplatte gespeichert.

Ich würde raten gegen ADWs encfs Lösung. Encfs verschlüsselt keine Volumes, verschlüsselt Dateien jedoch Datei für Datei und lässt dennoch viele Metadaten offen.


0
2018-05-04 05:54



der naheliegendste Ansatz wäre:

Ich sehe damit keine Probleme. nur sicher sein, dass Sie genügend Platz zuteilen.

Sie können in Echtzeit mit LUKS oder TrueCrypt verschlüsseln

bearbeiten:

Nach deinem Kommentar denke ich, dass ich jetzt dein Problem verstehe

Apache / PHP unterstützt dies nicht.

Sie können jedoch Ihren eigenen Deamon schreiben, eine Socket-Verbindung öffnen, um eingehende Daten zu überwachen und mit ihnen umzugehen, ganz wie Sie wollen. im Grunde schreiben Sie Ihren eigenen Webserver in PHP. sollte nicht zu viel Arbeit sein. Es gibt auch einige nette Klassen zur Verfügung. zend hat einige Server-Bibliotheken, die die HTTP-Behandlung erleichtern.

aber Sie könnten das viel einfacher in Perl tun. Sie können Post-Daten nur Chunk für Chunk eingeben, um den zugehörigen Speicher zu verarbeiten. php hat nur einen anderen Arbeitsablauf.


0
2018-05-07 00:58



Haben Sie überlegt, unter Linux eine RAMdisk zu erstellen?

http://www.vanemery.com/Linux/Ramdisk/ramdisk.html

Da dies als systemeigener Dateisystemspeicherort angezeigt wird, müssen Sie Ihre PHP-Instanz nur auf diesen Speicherort verweisen (vorausgesetzt, dass sie über die richtigen Berechtigungen verfügt).

Ich bin mir nicht sicher, was die Auswirkungen sind, wenn die Festplatte ausfällt, würde ich mir vorstellen, dass der Standort nicht mehr beschreibbar oder abwesend sein würde. Es kann weitere Auswirkungen auf die Performance und große Dateien geben, aber da dies nicht mein Fachgebiet ist, kann ich Ihnen nicht viel darüber erzählen.

Hoffe, das ist eine Hilfe.


-2
2018-04-18 11:55