Stocker les fichiers PHP(/PHP-FPM/Apache) temporaires-à-télécharger en RAM plutôt que dans le système de fichiers (ou encrypté seulement)?

question originale

donc le projet sur lequel je travaille est d'une paranoïa mortelle à propos des téléchargements de fichiers.

Dans le cadre de cette question, je n'utilise pas ce terme en ce qui concerne les charges utiles; je parle de confidentiality .

Les programmes

peuvent toujours planter et laisser des fichiers temporaires traîner dans le système de fichiers. C'est normal. Le légèrement confidentialité-paranoïaque peut écrire un cronjob qui frappe le dossier du fichier temporaire toutes les quelques minutes et efface tout ce qui est plus ancien que quelques secondes avant l'appel cronjob (pas tout , simplement parce qu'autrement il pourrait attraper un fichier en cours de téléchargement).

...malheureusement, nous poussons cette paranoïa un peu plus loin:

idéalement, nous aimerions Ne jamais voir de fichiers temporaires provenant de fichiers téléchargés ailleurs que dans la mémoire RAM associée au processus.

y a-t-il un moyen d'apprendre à PHP à rechercher des fichiers temporaires sous forme de blobs en mémoire plutôt que dans le système de fichiers? nous utilisons PHP-FPM comme gestionnaire CGI et Apache comme serveur web, au cas où cela nous faciliterait la tâche. (Note également:' Filesystem 'est le mot clé ici, plutôt que' disc', car il y a bien sûr des façons de mapper le système de fichiers en RAM, mais cela ne règle pas la question de l'accessibilité et du nettoyage automatique post-crash.)

alternativement, est il y a un moyen que ces fichiers temporaires puissent être cryptés immédiatement quand ils sont écrits sur le disque , de sorte qu'ils ne soient jamais conservés dans le système de fichiers sans cryptage?


Thread "vue d'ensemble de 151920920"

Je ne peux malheureusement accepter que un réponse - mais à quiconque lisant ceci, le fil entier est extrêmement précieux et contient le collectif idées de beaucoup de gens. Selon ce que vous espérez réaliser, la réponse acceptée peut ne pas être intéressante pour vous . Si vous êtes venu ici par un moteur de recherche, s'il vous plaît prendre un moment pour lire le fil complet .

voici une compilation des usecases telles que je les vois pour consultation rapide:

Re: les fichiers temporaires de PHP

  • RAM au lieu du disque (par exemple en raison de préoccupations d'E/S) → RAMdisk /comparable ( plasmid87 , Joe Hopfgartner )

  • cryptage immédiat (par système de fichiers-utilisateur) → encFS ( ADW ) (+ a gotcha as per Sander Marechal )

    1519590920"
  • fichier Sécurisé permissions → permissions restrictives de Linux natif (optionnellement par vhost ) ( Gilles ) ou SELinux (voir commentaires Divers)

  • mémoire attachée au processus au lieu du système de fichiers (donc un crash de processus supprime les fichiers) (initialement prévu par la question)

Re: vos fichiers, post-télécharger des

38
demandé sur Community 2011-04-18 14:25:06

10 réponses

Avez-vous envisagé de mettre une couche entre l'utilisateur et le serveur web? Utiliser quelque chose comme perlbal avec du code personnalisé devant le serveur web vous permettrait d'intercepter les fichiers téléchargés avant qu'ils ne soient écrits n'importe où, de les chiffrer, de les écrire sur un disque RAM local et puis de proxy la demande sur le serveur web proprement dit (avec le nom de fichier et la clé de déchiffrement des fichiers).

si le processus PHP se bloque, le fichier crypté est il est parti mais ne peut pas être décrypté. Aucune Donnée non chiffrée n'est écrite sur le disque (ram).

5
répondu Cal 2011-05-04 03:43:51

CGI à la rescousse!

si vous créez un répertoire cgi-bin et que vous le configurez correctement, vous obtiendrez le message via stdin (pour autant que je sache, les fichiers ne sont pas écrits sur le disque de cette façon).

donc, dans votre config apache ajouter

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>

écrit alors un script PHP en mode CGI pour analyser les données post. À partir de mes tests (limités), aucun fichier local ne semble avoir été créé. L'échantillon vide ce qu'il lit à partir de stdin ainsi que les variables d'environnement, pour vous donner une idée de ce qui est là pour travailler avec.

Exemple de script installé dans /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>

sortie de L'échantillon C'est la sortie (source) lorsque je télécharge un fichier en texte clair:

<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
répondu Phil Lello 2011-05-04 20:17:32

j'ai eu un flash d'inspiration sur ceci: black-hole filesystems.

essentiellement, il s'agit d'un faux système de fichiers, où les données ne sont jamais écrites, mais tous les fichiers existent, et n'ont pas de contenu.

il y a une discussion sur sur unix.se à propos de ces, et une réponse implique une mise en œuvre de fusible de juste ceci (cité ici):

ce n'est pas pris en charge n'importe quel unix que je connais, mais tu peux faire joli beaucoup de n'importe quoi avec FUSE . Il y a au moins une implémentation de nulls1 151970920" , un système de fichiers où chaque fichier existe et se comporte comme /dev/null (ce n'est-ce pas la seule implémentation que j'ai jamais vu).

"151910920 ¹ 1 à ne pas confondre avec le *BSD nullfs , qui est analogue à bindfs .

Je n'ai pas eu la chance de tester ce mais si vous configurez upload_tmp_dir à un emplacement de trou noir, le téléchargement (serait|devrait) Ne jamais être écrit sur le disque, mais être quand même disponible dans $HTTP_RAW_POST_DATA (ou php://input). Si ça marche, c'est mieux que de corriger PHP

4
répondu Phil Lello 2017-04-13 12:36:31

Je ne suis pas familier avec PHP, donc ma réponse ne sera pas directement dans un how-to, mais je pense que vous travaillez sous quelques idées fausses sur ce que la protection diverses caractéristiques du système fournissent, qui vous ont conduit à rejeter des solutions valables en faveur de solutions qui ont exactement les mêmes propriétés de sécurité. D'après vos commentaires, je crois comprendre que vous utilisez Linux; la plupart de ma réponse s'applique à d'autres unices, mais pas à D'autres systèmes tels que Windows.

autant que je puisse voir, vous êtes préoccupé par trois scénarios d'attaque:

  1. l'attaquante accède physiquement à la machine, l'éteint, sort le disque et en lit le contenu à son gré. (Si l'attaquant peut lire votre mémoire vive, vous avez déjà perdu.)
  2. L'attaquant peut exécuter du code en tant qu'utilisateur sur la machine.
  3. un bug dans les scripts CGI permet à un processus de lire des fichiers temporaires créés par d'autres processus.

la première sorte d'attaquant peut lire tout ce qui est non crypté sur le disque, et rien qui est crypté avec une clé qu'elle n'a pas .

ce que le second type d'attaquant peut faire dépend de si elle peut exécuter le code comme le même utilisateur qui exécute vos scripts CGI.

Si elle ne peut exécuter du code que d'autres utilisateurs, alors l'outil pour protéger les fichiers est autorisations . Vous devriez avoir une répertoire qui est le mode 700 (= drwx------ ), c'est-à-dire seulement accessible par un utilisateur, et appartenant à l'utilisateur qui exécute les scripts CGI. Les autres utilisateurs ne pourront pas accéder aux fichiers de ce répertoire. Vous n'avez pas besoin d'un chiffrement supplémentaire ou d'une autre protection.

si elle peut exécuter du code en tant qu'utilisateur CGI (ce qui, bien sûr, inclut l'exécution du code en tant que root), alors vous avez déjà perdu. Vous pouvez voir la mémoire d'un autre processus si vous exécutez le code comme le font les mêmes utilisateurs - débogueurs le temps! Sous Linux, vous pouvez facilement le voir par vous-même par explorer /proc/$pid/mem . Par rapport à la lecture d'un fichier, Lire la mémoire d'un processus est un peu plus difficile sur le plan technique, mais sur le plan de la sécurité, il n'y a pas de différence.

ainsi avoir les données dans les fichiers n'est pas en soi un problème de sécurité .

examinons maintenant la troisième préoccupation. Le problème est qu'un bug dans le CGI permet à l'attaquant de fouiner sur les fichiers mais pas d'exécuter du code arbitraire . Ceci est lié à un problème de fiabilité - si le processus de CGI meurt, il peut laisser des fichiers temporaires derrière. Mais c'est plus général: le fichier peut être lu par un script courant simultanément.

la meilleure façon de se protéger contre cela est en effet d'éviter que les données soient stockées dans un fichier. Cela devrait être fait au niveau de PHP ou de ses bibliothèques, et je ne peux pas aider avec cela. Si il n'est pas possible, alors nullfs comme suggéré par Phil Lello est une solution raisonnable: le processus PHP sera de penser qu'il est l'écriture de données à un fichier, mais le fichier ne sera jamais réellement contenir des données.

il y a un autre truc courant sur unix qui pourrait être utile ici: une fois que vous avez créé un fichier, vous pouvez unlink (supprimer) et continuer à travailler avec. Dès qu'il est déconnecté, le fichier impossible d'accéder par son ancien nom, mais les données restent dans le système de fichiers tant que le fichier est ouvert dans au moins un processus. Cependant, c'est surtout utile pour la fiabilité, pour obtenir le système d'exploitation pour supprimer les données lorsque le processus meurt pour une raison quelconque. Un attaquant qui peut ouvrir des fichiers arbitraires avec les permissions du processus peut accéder aux données via /proc/$pid/fd/$fd . Et un attaquant qui peut ouvrir des fichiers à tout moment, a une petite fenêtre entre la création du fichier et son dissociation: si elle peut ouvrir le fichier alors, elle peut regarder les données qui y sont ajoutées par la suite. Cette protection peut néanmoins être utile, car elle transforme l'attaque en une attaque sensible au temps et peut nécessiter de nombreuses connexions simultanées, ce qui pourrait être contrecarré ou au moins rendu beaucoup plus difficile par un limiteur de débit de connexion.

3
répondu Gilles 2017-05-23 12:17:29

avez-vous cherché à utiliser FUSE pour créer un répertoire crypté qui ne peut être consulté que par un utilisateur spécifique?

http://www.arg0.net/encfs

la mémoire ne sera pas associée à un processus spécifique mais les fichiers ne seront accessibles qu'à un utilisateur spécifique (le même que celui que votre serveur web exécute pour être utile!) qui peut être suffisant?

1
répondu ADW 2011-04-20 16:03:05

PHP stockera les fichiers téléchargés dans le système de fichiers avant que votre script ait la chance d'intercepter les données php://input et $HTTP_RAW_POST_DATA sera vide dans ce cas (même si vous mettez file_uploads = Off ).

pour les petits fichiers, vous pouvez essayer de définir <form enctype="application/x-www-form-urlencoded" ... mais je n'ai pas réussi à l'utiliser. Je vous suggère de recompiler php et de commenter la partie traitant les téléchargements de fichiers comme dans ce rapport de bogue (commentaire de pollita@php.net).

Pro: Aucun téléchargement de fichier, Données en php: / / input Con: Recompiler, pas de support du Fournisseur

1
répondu Stephan B 2011-05-03 09:40:09

vos préoccupations sont valables. Il existe quelques solutions à ce problème. L'un est de stocker le fichier dans une base de données. Une base de données NoSQL comme MongoDB ou CouchDB est construite pour stocker efficacement des fichiers. MySQL est une autre option et présente des avantages par rapport à NoSQL. Une base de données relationnelle comme MySQL rend très facile d'implanter le contrôle d'accès parce que vous pouvez relier la table files et users par une clé primaire.

dans MySQL vous pouvez utiliser le longblob type de données contient 2^32 bits ou environ 500 mo. Vous pouvez créer une table qui est résident de mémoire en utilisant le moteur de mémoire: CREATE TABLE files ENGINE=MEMORY ... . Encore plus. MySQL a le cryptage sous la forme de aes_encrypt() et des_encrypt() mais ils utilisent tous les deux mode ECB qui est poubelle .

$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')"); 

il suffit de sélectionner le fichier et l'utiliser comme vous le feriez $sensi_file . Gardez à l'esprit que vous êtes en train d'échapper à l'entrée pour obtenir he caractères littéraux et donc stockage du binaire brut.

0
répondu rook 2011-05-02 20:20:21

vous pouvez créer un tmpfs et le monter avec un masque approprié. De cette façon, les seuls processus qui peuvent lire des fichiers sont les utilisateurs qui l'ont créé. Et, parce que c'est un tmpfs , rien n'est jamais stocké sur le disque.

je vous conseille contre ADW encfs solution. Encfs ne crypte pas les volumes, mais il crypte les fichiers fichier par fichier, laissant encore un grand nombre de métadonnées exposées.

0
répondu Sander Marechal 2011-05-04 05:54:39

le plus évident de l'approche serait:

Je ne vois aucun problème avec ça. assurez-vous juste d'allouer assez d'espace difficile.

vous pouvez chiffrer en temps réel avec LUKS ou truecrypt

edit:

après votre commentaire je pense que je comprends maintenant votre problème""

apache / php ne supporte pas cela.

vous pouvez cependant écrire votre propre deamon, ouvrir une connexion socket pour écouter et traiter les données entrantes dans ce que vous voulez. en gros, écrire votre propre webserver en php. ne devrait pas être trop de travail. il y a aussi de belles classes disponibles. zend dispose de bibliothèques de serveurs qui facilitent le traitement http.

mais toi ça pourrait être plus facile en perl. vous pouvez simplement le fichier de données de postes morceau par morceau dans le processus de la mémoire associée. php a juste un flux de travail différent.

0
répondu The Surrican 2011-05-08 01:38:07

avez-vous envisagé de créer un disque dur sous Linux?

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

puisque cela apparaîtra comme un emplacement de système de fichiers natif, vous n'avez qu'à pointer votre instance PHP (en supposant qu'elle ait les permissions correctes) à cet emplacement.

Je ne suis pas sûr de ce que sont les ramifications si le disque tombe en panne, j'imagine que l'emplacement deviendrait Non-enregistrable ou absent. Il peut y avoir d'autres ramifications entourant le rendement et les gros dossiers, mais comme ce n'est pas mon domaine d'expertise, Je ne peux pas vous en dire beaucoup à ce sujet.

j'Espère que cela est de peu d'aide.

-2
répondu plasmid87 2011-04-18 11:55:13