Подтвердить что ты не робот

Хранение файлов PHP (/PHP-FPM/Apache) временных файлов из ОЗУ, а не файловой системы (или только зашифрованной)?

Оригинальный вопрос

Итак, проект, над которым я работаю, смертельно параноик о загрузке файлов.
В рамках этого вопроса я не использую этот термин в отношении полезных нагрузок; Я говорю о конфиденциальности.

Программы всегда могут выходить из строя и оставлять временные файлы в файловой системе. Это нормально. Немного конфиденциальность-параноид может написать cronjob, который каждые несколько минут врезается в временную папку с файлом, и удаляет что-либо более чем за несколько секунд до вызова cronjob (не все, просто потому, что в противном случае он мог бы поймать файл при загрузке).

... К сожалению, мы делаем этот параноик еще одним шагом:

В идеале нам бы хотелось никогда не видеть временные файлы из загрузки файлов в любом месте, кроме связанного с процессом ОЗУ.

Есть ли способ научить PHP искать временный файл как капли в памяти, а не в файловой системе? Мы используем PHP-FPM как обработчик CGI и Apache в качестве нашего веб-сервера, в случае, если делает это легче. (Примечание: "Файловая система" - это ключевое слово здесь, а не "диск", поскольку, конечно, есть способы сопоставить файловую систему с ОЗУ, но это не устраняет проблему доступности и автоматической очистки после сбоя. )

Альтернативно, есть ли способ, по которому эти временные файлы могут быть зашифрованы немедленно, когда они записываются на диск, чтобы они никогда не содержались в файловой системе без шифрования?


Обзор темы

Я, к сожалению, могу только принять один ответ - но для всех, кто это читает, вся нить чрезвычайно ценна и содержит коллективные идеи многих людей. В зависимости от того, чего вы надеетесь достичь, принятый ответ вам может быть неинтересным. Если вы пришли сюда через поисковую систему, , пожалуйста, найдите минутку, чтобы прочитать весь поток.

Вот компиляция usecases, как я их вижу для быстрой справки:

Re: Временные файлы PHP

  • ОЗУ вместо диска (например, из-за проблем с вводом/выводом) → RAMdisk/сопоставимый (plasmid87, Joe Hopfgartner)

  • Немедленное шифрование (per-filesystem-user) и rarr; encFS (ADW) (+ заглавие в соответствии с Sander Marechal)

  • Защитить права доступа к файлам и rarr; ограничивающие собственные разрешения для Linux (необязательно для vhost) (Gilles) или SELinux (см. различные комментарии)

  • Память, привязанная к процессу, вместо файловой системы (поэтому сбой процесса удаляет файлы) (первоначально заданный вопросом)

Re: ваши файлы, post-upload

4b9b3361

Ответ 1

Рассматривали ли вы размещение слоя между пользователем и веб-сервером? Используя что-то вроде perlbal с некоторым пользовательским кодом перед веб-сервером, вы сможете перехватить загруженные файлы, прежде чем они будут записаны в любом месте, зашифруйте их, напишите их на локальный ramdisk и затем проксируйте запрос на самом веб-сервере (с именем файла и ключем дешифрования к файлам).

Если процесс PHP выходит из строя, зашифрованный файл остается открытым, но не может быть расшифрован. Никакие незашифрованные данные не записываются на диск (ram).

Ответ 2

CGI на помощь!

Если вы создадите каталог cgi-bin и настроитесь соответствующим образом, вы получите сообщение через stdin (насколько я могу судить, файлы вообще не записываются на диск).

Итак, в вашей конфигурации apache добавьте

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>

Затем напишите CGI-режим PHP script для анализа данных сообщения. Из моего (ограниченного) тестирования локальные файлы не создаются. Образец сбрасывает то, что он читает из stdin, а также переменные среды, чтобы дать вам представление о том, с чем там работать.

Пример script, установленный как /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>

Образец вывода Это вывод (источник) при загрузке текстового файла:

<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 => [email protected]
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>

Ответ 3

У меня была вспышка вдохновения: файловые системы с черными дырами.

По сути, это поддельная файловая система, где данные никогда не записываются, но все файлы существуют и не имеют содержимого.

Здесь обсуждение unix.se об этом, а один ответ включает реализацию FUSE только это (цитируется здесь):

Это не поддерживается любой unix, которого я знаю, но вы можете сделать довольно многое другое с FUSE. Там как минимум одна реализация из nullfs¹, файловая система, где каждый файл существует и ведет себя как /dev/null (это это не единственная реализация, которую я имею когда-либо видел).

¹ Не путать с * BSD nullfs, что аналогично bindfs.Суб >

У меня не было возможности протестировать этот, но если вы установите upload_tmp_dir в место черных дыр, загрузка (будет | должна) никогда не будет записана на диск, но будет доступна в $HTTP_RAW_POST_DATA (или php://ввод). Если это работает, это лучше, чем исправление PHP

Ответ 4

Я не знаком с PHP, поэтому мой ответ не будет отображаться непосредственно в инструкции, но я думаю, что вы работаете под некоторыми неправильными представлениями о том, какую защиту предоставляют различные системные функции, которые заставили вас отказаться от действительных решений в пользу решений, которые имеют точно такие же свойства безопасности. По твоим комментариям, я понимаю, что вы используете Linux; большая часть моего ответа относится к другим устройствам, но не к другим системам, таким как Windows.

Насколько я вижу, вы обеспокоены тремя сценариями атаки:

  • Злоумышленник получает физический доступ к компьютеру, отключает его, вытаскивает диск и считывает его содержимое в свое распоряжение. (Если злоумышленник может прочитать вашу RAM, вы уже потеряли.)
  • Злоумышленник может запускать код как пользователь на машине.
  • Ошибка в сценариях CGI позволяет процессу читать временные файлы, созданные другими процессами.

Первый тип злоумышленника может читать все, что незашифровано на диске, и ничего, что зашифровано с помощью ключа, которого у нее нет.

Что может сделать второй тип злоумышленника, зависит от того, может ли он запускать код как того же пользователя, который запускает ваши сценарии CGI.

Если она может запускать только код в качестве других пользователей, тогда инструмент для защиты файлов разрешений. У вас должен быть каталог с режимом 700 (= drwx------), то есть доступным только пользователю и принадлежащим пользователю, выполняющему сценарии CGI. Другие пользователи не смогут получить доступ к файлам в этом каталоге. Вам не требуется дополнительное шифрование или другая защита.

Если она может запускать код в качестве пользователя CGI (который, конечно же, включает запуск кода с правами администратора), вы уже потеряли его. Вы можете видеть память другого процесса, если вы используете код как один и тот же пользователь - отладчики делают это все время! Под Linux вы можете легко убедиться в этом > /proc/$pid/mem. По сравнению с чтением файла чтение памяти процесса немного сложнее, но с точки зрения безопасности, нет никакой разницы.

Таким образом, , имеющий данные в файлах, сам по себе не является проблемой безопасности.

Теперь рассмотрим третью проблему. Опасность в том, что ошибка в CGI позволяет злоумышленнику отслеживать файлы, но не запускать произвольный код. Это связано с проблемой надежности - если процесс CGI умирает, он может оставить временные файлы позади. Но это более общее: файл может быть прочитан одновременно запущенным script.

Лучшим способом защиты от этого является то, чтобы вообще не хранить данные, хранящиеся в файле. Это должно быть сделано на уровне PHP или его библиотек, и я не могу с этим поделать. Если это невозможно, то nullfs как предложенный Филом Лелло, является разумным Обходной путь: процесс PHP будет думать, что он записывает данные в файл, но файл никогда не будет содержать никаких данных.

Еще один общий трюк Unix, который может быть полезен здесь: после создания файла вы можете отсоединить (удалить) его и продолжить работу с ним. Как только он отсоединяется, к файлу не может быть доступа его прежнее имя, но данные остаются в файловой системе, пока файл открыт, по крайней мере, в одном процессе. Однако это в основном полезно для надежности, чтобы заставить ОС удалять данные, когда процесс умирает по любой причине. Злоумышленник, который может открывать произвольные файлы с разрешениями процесса, может получить доступ к данным через /proc/$pid/fd/$fd. И злоумышленник, который может открывать файлы в любое время, имеет небольшое окно между созданием файла и его отключением: если она сможет открыть файл, то она сможет просматривать данные, добавленные в него впоследствии. Тем не менее, это может быть полезной защитой, так как она превращает атаку в одну, которая чувствительна к времени, и может потребовать много одновременных соединений, поэтому может препятствовать или, по крайней мере, значительно затруднять ограничение скорости соединения.

Ответ 5

Вы изучили использование FUSE для создания зашифрованного каталога, доступ к которому может получить только конкретный пользователь?

http://www.arg0.net/encfs

Память не будет связана с конкретным процессом, но файлы будут доступны только определенному пользователю (то же, что ваш веб-сервер работает, чтобы быть полезным!), которого может быть достаточно?

Ответ 6

PHP будет хранить загруженные файлы в файловой системе, прежде чем ваш script сможет перехватить данные php://input и $HTTP_RAW_POST_DATA в этом случае будет пустым (даже если вы установите file_uploads = Off).

Для небольших файлов вы можете попробовать установить <form enctype="application/x-www-form-urlencoded" ..., но мне это не удалось. Я предлагаю вам перекомпилировать php и прокомментировать файлы загрузки файлов, как в этом Отчет об ошибке (Комментарий от [email protected]).

Pro: загрузка файлов вообще отсутствует, данные в php://input Con: перекомпилировать, нет поддержки поставщиков.

Ответ 7

Ваши опасения действительны. Есть несколько решений этой проблемы. Один из них - хранить файл в базе данных. База данных NoSQL, такая как MongoDB или CouchDB, предназначена для эффективного хранения файлов. MySQL - еще один вариант и имеет преимущества перед NoSQL. Реляционная база данных, такая как MySQL, очень упрощает имплантацию контроля доступа, поскольку вы можете связать таблицу files и users с помощью первичного ключа.

В MySQL вы можете использовать тип данных longblob, содержащий 2 ^ 32 бита или около ~ 500 мб. Вы можете создать таблицу с резидентной памятью с помощью механизма MEMORY: CREATE TABLE files ENGINE=MEMORY .... Дальше больше. MySQL имеет шифрование в виде aes_encrypt() и des_encrypt(), но оба они используют режим ECB, который является мусором.

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

Просто выберите файл и используйте его так же, как и $sensi_file. Имейте в виду, что вы избегаете ввода, чтобы получить литературные символы и, таким образом, сохранить исходный двоичный файл.

Ответ 8

Вы можете создать tmpfs и установить его с помощью надлежащего umask. Таким образом, единственными процессами, которые могут читать файлы, являются пользователи, которые его создали. И поскольку это tmpfs, на диске ничего не сохраняется.

Я бы посоветовал отказаться от решения ADW encfs. Encfs не шифрует тома, но шифрует файлы по каждому файлу, все еще оставляя много метаданных.

Ответ 9

наиболее очевидный подход:

Я не вижу никаких проблем с этим. просто убедитесь, что вы выделяете достаточно места.

вы можете шифровать в реальном времени с помощью LUKS или truecrypt

изменить:

после вашего комментария я думаю, что теперь понимаю вашу проблему

apache/php не поддерживает это.

вы можете, однако, написать свой собственный деамон, открыть соединение сокета для прослушивания и обработки входящих данных любым способом. в основном записывая собственный веб-сервер в php. не должно быть слишком много работы. есть также некоторые хорошие классы. zend имеет некоторые серверные библиотеки, которые упрощают обработку http.

но вы можете сделать это намного проще в perl. вы можете просто записать кусок пост-данных куском для обработки связанной памяти. php просто имеет другой рабочий процесс.

Ответ 10

Рассматривали ли вы создание RAMdisk под Linux?

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

Поскольку это будет отображаться как локальное расположение файловой системы, вам нужно указать только ваш экземпляр PHP (при условии, что он имеет правильные разрешения) в этом месте.

Я не уверен, что такое разветвления, если диск выходит из строя, я бы предположил, что местоположение станет неприемлемым или отсутствующим. Там могут быть дополнительные последствия, связанные с производительностью и большими файлами, но поскольку это не моя область знаний, я не могу рассказать вам об этом.

Надеюсь, это поможет.