Выполнение php-файлов в папке upload

Случаются ситуации, когда php-скрипты исполняются в директории хранения загружаемых файлов или другими словами php-файлы выполняются в папке upload, это есть уязвимость, дыра на сайте, и это касается любой CMS, не только 1С Битрикс.

В папке /upload/ ничего не должно запускаться и выполняться, все загруженные изображения, документы, архивы и т.д. все это доступно для скачивания любому пользователю и встраивается в контент, оно безопасно (за исключением .gif изображений), потому что это не исполняемые файлы, а есть исполняемые файлы, которые можно открыть в браузере и они выполнят какой-то код на вашем сайте, вообще любой, пример расширений исполняемых файлов.
.php .php3 .php4 .php5 .php6 .phtml .pl .asp .aspx .cgi .dll .exe .shtm .shtml .fcg .fcgi .fpl .asmx .pht

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

Есть конечно различные защиты на уровне JS, PHP, .htaccess переименование файлов в форме и т.д. но эта ситуация со временем может изменяться, сама форма переделывается, модуль другой устанавливается, меняются разработчики сайтов, изменяется ПО сервера, Apache заменили на Nginx, меняются панели управления сервером и т.д., но это не дает 100% гарантии защиты, за этим должен следить владелец сайта регулярно, хоть всю жизнь, хотите вы этого или нет, все в мире изменяется.

Если все еще не поняли о чем речь, создайте в папке /upload/ любой php-файл, например i.php и вставьте в него данный код

<?php
phpinfo();

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

https://example.com/upload/i.php

Если php-скрипт выполняется, вы увидите информацию о текущей версии php, т.е. дыра на вашем сайте открыта для всех желающих, ее 100% необходимо закрывать.

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

Проблема оказалась в самом файле .htaccess, который в папке /upload/, почему-то он у меня отличается от других сайтов на Битриксе, не все правила были добавлены, а проявила себя проблема из-за конфигов хоста для Apache2

/etc/apache2/vhosts/user1/example.com

Панель ISP добавляет в этот конфиг такие вот правила для php-фaйлов, принудительно делает их исполняемыми и если в .htaccess сайта это правило не будет переопределено, то все php-скрипты будут по умолчанию исполняемые, например, панель VESTA такого не делает по умолчанию.

<FilesMatch "\.ph(p[3-5]?|tml)$">
SetHandler application/x-httpd-php
</FilesMatch>
<FilesMatch "\.phps$">
SetHandler application/x-httpd-php-source
</FilesMatch>

Пример .htaccess для защиты папки /upload/

Это готовый, рабочий пример файла, его можно смело загружать в папку /upload/ и особо не беспокоиться, в большинстве случаев он будет работать даже после переезда на другую панель, но только если PHP будет запущен как модуль Apache2.

<IfModule mod_mime.c>
<Files ~ "\.(php[2-7]?|phtml|pl|asp|aspx|cgi|dll|exe|ico|shtm|shtml|fcg|fcgi|fpl|asmx|pht|py|psp|rb|var|jsp)$">
SetHandler text/plain
ForceType text/plain
#Deny from all
</Files>
</IfModule>
<IfModule mod_mime.c>
RemoveHandler .php .php3 .php4 .php5 .php6 .phtml .pl .asp .aspx .cgi .dll .exe .shtm .shtml .fcg .fcgi .fpl .asmx .pht
AddType text/plain .php .php3 .php4 .php5 .php6 .phtml .pl .asp .aspx .cgi .dll .exe .shtm .shtml .fcg .fcgi .fpl .asmx .pht
</IfModule>
<IfModule mod_php5.c>
php_flag engine off
</IfModule>
<IfModule mod_php7.c>
php_flag engine off
</IfModule>

Советы

В моем случае не переопределена была директива <Files>, вот если бы ее не было в глобальных конфигах хоста Apache2, не пришлось бы переопределять, тогда сработало бы правило ниже RemoveHandler  + AddType text/plain, в случае с <Files> они почему-то не действуют.

Правила хоста Apache2 применяются глобально для всего сайта, поэтому необходимо переопределять их на сайте в файлах .htaccess

Данный файл .htaccess понимает только Apache2, если вы переезжаете на Nginx + PHP FPM, то это уже совсем другая история, в конфигах Nginx будет что-то типа:

location ~* /upload/.*\.(php|php3|php4|php5|php6|phtml|pl|asp|aspx|cgi|dll|exe|shtm|shtml|fcg|fcgi|fpl|asmx|pht|py|psp|rb|var)$ {
types {
text/plain text/plain php php3 php4 php5 php6 phtml pl asp aspx cgi dll exe ico shtm shtml fcg fcgi fpl asmx pht py psp rb var;
}
}
location ~* ^/bitrix/(modules|local_cache|stack_cache|managed_cache|php_interface) {
deny all;
}
location ~* /\.ht { deny all; }
location ~* /\.(svn|hg|git) { deny all; }
location ~* ^/upload/1c_[^/]+/ { deny all; }


Обратите внимание, в моем примере выше в правилах .htaccess есть одна закомментированная для примера строчка в директиве <Files>

#Deny from all

Если она закомментирована, то по умолчанию все выполняемые файлы сервер будет отдавать в виде текста, в основном так и делается.

Но если файл будет очень большого размера, выводить его в браузер тоже не самый хороший вариант, лучше вообще их закрыть и не показывать.
Если раскомментировать правило Deny from all, то доступ к выполняемому файлу будет закрыт полностью, такое сообщение выводится сервером.


Обратите внимание, иногда пользовательские правила в .htaccess не работают, не переопределяются, тогда смотрите, например, в этом файле в панели ISP

/etc/apache2/conf.d/z1_home.conf

Конкретно привилегии AllowOverride, если будет AllowOverride None то это будет запрещать, должно быть либо AllowOverride All либо вот так

AllowOverride FileInfo AuthConfig Limit Indexes Options=All,MultiViews

Если такого файла нет, то смотрите главный конфиг Apache2

/etc/apache2/apache2.conf

В нем может быть вот такое правило, применяется глобально ко всем сайтам в папке /var/www

<Directory /var/www/>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>


Обратите внимание, еще на это правило mod_php7.c для PHP7, раньше было mod_php5.c для PHP5, т.к. это отдельный модуль Apache2, нужно добавлять отдельно

<IfModule mod_php5.c>
php_flag engine off
</IfModule>
<IfModule mod_php7.c>
php_flag engine off
</IfModule>

Например, для включения антивируса 1С Битрикс нужно добавить уже отдельное правило для php7, для php5 правило работать не будет на PHP 7.1

<IfModule mod_php5.c>
php_value auto_prepend_file "/var/www/user1/data/www/example.com/bitrix/modules/security/tools/start.php"
</IfModule>

<IfModule mod_php7.c>
php_value auto_prepend_file "/var/www/user1/data/www/example.com/bitrix/modules/security/tools/start.php"
</IfModule>


Обратите внимание, еще бывает правило php_flag engine off не работает в .htaccess

<IfModule mod_php5.c>
php_flag engine off
</IfModule>
<IfModule mod_php7.c>
php_flag engine off
</IfModule>

Это правило выключает выполнение PHP-файлов в данной директории, оно не будет работать, если где-нибудь в конфигах хоста для Apache2 будет глобально задано правило с помощью php_admin_flag панель ISP именно так и делает

<Directory /var/www/user1/data/www/example.com>
Options +Includes -ExecCGI
<IfModule php5_module>
php_admin_flag engine on
</IfModule>
<IfModule php7_module>
php_admin_flag engine on
</IfModule>
</Directory>

Конфиги хостов в ISP хранятся здесь отдельно для каждого пользователя и хоста/домена

/etc/apache2/vhosts/user1/example.com

Почему не удается переопределить данное правило, можно узнать в официальной php-документации
http://php.net/manual/ru/configuration.changes.php

php_admin_value name value
Устанавливает значение указанной директивы. Не может быть использовано в файлах .htaccess.
Директивы любого типа, установленные с помощью php_admin_value не могут быть переопределены через .htaccess или ini_set().
Чтобы очистить предыдущее значение используйте значение none.

php_admin_flag name on|off
Используется для установки директивам логических значений.
Не может быть использовано в файлах .htaccess.
Директивы любого типа, установленные с помощью php_admin_flag не могут быть переопределены через .htaccess или ini_set().
Имя *
Логин (мин. 3 символа)
E-mail *
*— обязательные для заполнения поля
Логин или e-mail
TUNING-SOFT.RU Разработка умных веб-сервисов