Хранить файлы PHP (/ PHP-FPM / Apache) с временной загрузкой в ​​оперативной памяти, а не в файловой системе (или только в зашифрованном виде)? - PullRequest
38 голосов
/ 18 апреля 2011

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

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

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

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

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

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

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


Обзор темы

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

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

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

  • RAM вместо диска (например, из-за проблем с вводом / выводом) & rarr; RAMdisk / сопоставимый ( plasmid87 , Джо Хопфгартнер )

  • Немедленное (для каждой файловой системы) шифрование & rarr; encFS ( ADW ) (+ Получено согласно Сандер Марешал )

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

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

Re: ваши файлы, пост-загрузка

Ответы [ 10 ]

5 голосов
/ 04 мая 2011

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

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

4 голосов
/ 06 мая 2011

Меня вдохновило это: файловые системы с черными дырами.

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

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

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

¹ Не путать с * BSD nullfs , который аналогичен bindfs .

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

4 голосов
/ 04 мая 2011

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>

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

Пример сценария , установленный как / var / www // cgi-bin / test

<code>#!/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";
?>

Пример вывода Это вывод (источник), когда я загружаю текстовый файл:

<code><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


3 голосов
/ 06 мая 2011

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

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

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

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

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

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

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

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

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

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

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

1 голос
/ 03 мая 2011

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

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

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

1 голос
/ 20 апреля 2011

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

http://www.arg0.net/encfs

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

0 голосов
/ 07 мая 2011

самый очевидный подход будет:

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

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

редактировать:

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

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

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

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

0 голосов
/ 04 мая 2011

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

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

0 голосов
/ 03 мая 2011

Ваши опасения действительны. Есть несколько решений этой проблемы. Одним из них является сохранение файла в базе данных. База данных 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. Имейте в виду, что вы избегаете ввода, чтобы получить символьные литералы и, таким образом, сохраняете необработанный двоичный файл.

0 голосов
/ 18 апреля 2011

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

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

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

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

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

...