0,0
рейтинг
11 июня 2009 в 00:13

Разработка → Превентивная защита ваших и не ваших скриптов

PHP*
Наверное не ошибусь если скажу, что очень большая часть мегахакерских_взломов основаны на закачке PHP-скрипта в каталог, доступный для записи в него файлов скриптами (0777, например). Это каталоги для заливки фотографий товаров, аватарок и т.п.

Я еще несколько лет назад «допер» до способа предотвратить на корню подобные вещи. Закачать — может и закачают, а воспользоваться не смогут. Это казалось очевидным все это время, но мало кто это реализовывает. К примеру, только в последнем патче популярного форума SMF была добавлена подобная штука.
Поэтому, для тех, кто не задумывался…

Всё просто. Во все каталоги, доступные для записи, закачиваем (или добавляем строчки к существующему) .htaccess с содержимым:

php_flag engine 0
AddType "text/html" .php .cgi .pl .fcgi .fpl .phtml .shtml .php2 .php3 .php4 .php5 .asp .jsp

Этим самым мы отключаем PHP в данном каталоге и заставляем все скрипты отображаться как HTML.
Это можно сделать просто на всякий случай. Лишним уж точно не будет.
Разумеется, только для Apache. Если кто-то знает как подобное реализовать в IIS — напишите. :)
Владимир Плужников @varyen
карма
16,5
рейтинг 0,0
Пользователь
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (90)

  • +1
    Люблю так делать на своих горячо любимых проектах. Впервые такую штуку увидел в IPB.
    Хотя php_flag engine 0 для меня в новинку.
  • +4
    Самое забавное, что можно попробовать загрузить и перезаписать сам .htaccess :)
    Поэтому нужен каталог с .htaccess, а в нем уже каталог с файлами.
    Ну а самый лучший вариант это все же белый список с рассширениями.
    • +7
      Запретить конкретно у .htaccess запись и всё. :)
      • 0
        Это усложняет решение и лишает его элегантности, но тоже вариант.
      • 0
        Это не спасет от переименования и залития нового файла с именем .htaccess
        • 0
          Переименовать ещё нужно умудриться.
        • 0
          А где я говорил, что это абсолютная защита? Это от тупейших случаев, которые, тем не менее, встречаются очень часто.
        • +1
          с позиции линукс-админа — chattr +i .htaccess (при условии что фс — ext*)
          Википедия
          • 0
            Забыли упомянуть, что выполнять нужно от рута, т.е. на шаред хостинге прием не действует.
            На фре — chflags uchg filename.
    • 0
      .htaccess нужно класть в другую директорию, повыше, оформляя в Directory или DirectoryMatch.
      • 0
        Кстати да, так более централизовано.
      • 0
        Directory* в .htaccess нельзя, Files* можно
        • 0
          Точно! Как я забыл? Помню как сам хотел обойтись одним файлом .htaccess — но не срослось.
      • –1
        Я, собственно, про это и намекал.
    • +1
      А если в ту дерикторию залить .htaccess c php_flag engine 1?
      Т.к. свойства наследуются, то должно заработать.
      • 0
        Если htaccess будет выше уровнем или в него будет запрещена запись, всё будет ок
        • +1
          dir/
          .htacess (php_flag engine 0)
          safe_upload_dir/
          .htacess (php_flag engine 1, который тоже залить со скриптом)
          hack.php (думаю это выполниться)

          Поэтому «Поэтому нужен каталог с .htaccess, а в нем уже каталог с файлами.» не прокатит :)
          • 0
            Тогда права на запись :)
    • 0
      В httpd.conf Apache2.2 есть следующие строки:
      <FilesMatch "^\.ht">
          Order allow,deny
          Deny from all
          Satisfy All
      </FilesMatch>

      Если не ошибаюсь, они как раз для этого и служат.
      • 0
        Это служит что бы пользователь не мог скачать .htaccess обращаясь например example.com/.htaccess
        К Php это отношение не имеет
  • +14
    Я обычно весь загружаемый контент выношу на отдельный поддомен, на котором вообще все скрипты отключены. В итоге не надо дополнительно настраивать конфигурацию веб-сервера для каталога + если поддомен на другом сервере, то и на скорости загрузки страницы это положительно скажется.
    • 0
      Интересна техническая сторона. Как файлы загруженные скриптами положить на другой сервер? Обычным аплоадом здесь не обойтись.
      • 0
        Это вы не подумав спросили :). Поддомен, домен. Соседняя директория с различной степенью «соседства».
        • 0
          ну почему же, соседство поддоменов — дело ясное
          но "+ если поддомен на другом сервере" в комментарии присутствует
          или же это уже чисто техническая составляющая серверов, а для скрипта- это будет происходит прозрачно и не нужно заморачиваться по поводу того, что поддомен физически находится на другом сервере.
          • 0
            По фтп можно заливать, но как-то это некрасиво :)
      • +2
        В большинстве случаев поддомен находится на том же сервере.
        Если поддомен находится на другом сервере, то решить проблему можно так.
        есть site.ru
        все статические файлы находятся на i.site.ru
        мы создаем временную папку site.ru/temp/, в которую кладем наши файлы. Там же файл htaccess ридонли, в котором никому доступ, кроме IP сервера i.site.ru

        на i.site.ru по крону или как только загружен файл на site.ru/temp/ применяется такая конструкция:

        $uploaddir = «temp_folder/»;

        $host = $_POST[«host»]; //ccылка вида habrahabr.ru/file.jpg

        $expansion = strtolower(strrchr($host, ".")); //выясняем расширение файла в строке
        $filename = $uploaddir.«temp_file».$expansion; //полный путь загруженного файла

        ini_set('max_execution_time',600);//устанавливаем время работы скрипта

        //закачка
        $fp=fopen($filename,«w»);//создаем пустой файл
        fclose($fp);
        $ch=curl_init();
        curl_setopt($ch, CURLOPT_URL, $host);//запускаем сеанс curl
        $fp=fopen($filename,«w+»);//открываем файл для записи
        curl_setopt($ch, CURLOPT_FILE, $fp);// записываем в файл
        curl_setopt($ch, CURLOPT_REFERER, $host);
        curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
        curl_exec ($ch);//выполняем команды curl
        curl_close ($ch);//завершаем сеанс curl
        fclose ($fp);//закрываем файл

        $new_file = «новая папка/новое имя файла»; //полный НОВЫЙ путь загружаемого файла

        rename($filename, $new_file); //перенесли файл в новое место

        @unlink( $filename );


        Т.е. мы перекидываем файлы на отдельный домен. Ну и как сказал Insbrook:
        Я обычно весь загружаемый контент выношу на отдельный поддомен, на котором вообще все скрипты отключены.
        • 0
          Спасибо за подробный ответ. Идея очень хорошая, над реализацией надо будет поработать.
  • +1
    как насчёт
    <Files ~ "\.(php[2-5]?|cgi|pl|fcgi|fpl|phtml|shtml|asp|jsp)$">
        Deny from all
    </Files>
    
    • +2
      Попробуйте скопировать в эту папку file.php.txt (с каким-нибудь echo) и открыть в браузере =)
      • 0
        И что будет?
        Мне правда интересно.
        • 0
          Если в настройках сервера не прописан ContentType для .txt, он может запуститься как php
          • 0
            Все немного сложнее
            seclists.org/bugtraq/2004/Dec/0211.html
            www.net-security.org/vuln.php?id=3896
            Разработчики называют это «фичей».

            А файл можно переименовать и в file.php.xxx (найти неуказанное расширение не проблема).
            • +2
              Именно поэтому очень полезно php_flag engine 0 :)
            • –1
              Разработчикам апача надо за такое отрывать руки, так как это открывает потенциальную уязвимость.

              Хакеру на заметку: многие службы позволяют зарузить файл rar, но сервера при этом не знают такого расширения, и отдают его как text/plain, вполне возможно что file.php.rar может произвести желаемый эффект. Найти бы парочку адресов для проверки :)
              • 0
                Может и производит, проверял :)
                • 0
                  Идиотизм, ведь такую уязвимость очень легко оставить. Параноидальные идеи, типа проверять все части расширения, глупы, так как имя файла вполне может содержать .php., например manual.php.chm. Фигня какая -то, мне даже сказать нечего, почему нельзя было сделать опцию множественных расширений по умолчанию отключеннной. Апач — кривая фигня, чесслово, из-за таких вот «багофич» (аналогичная багофича, это выставление неправильного DOCUMENT_ROOT при использовании VirtualDocumentRoot), которые придуманы только чтобы ломать мозг разработчикам.
  • 0
    .htaccess
    Options None
    Options +FollowSymLinks

    не?
  • 0
    к сожалению, хоть это и действенный способ, способ не «универсальный». Хотя лучшего предложить не могу.
  • 0
    Это казалось очевидным все это время, но мало кто это реализовывает.

    Если вы уверены, что ваш проект всегда будет работать на Apache, то почему бы и нет?

    Но если он может работать еще и на других серверах, то все равно приходится задуматься о защите в самих скриптах. Ну а раз об этом пришлось задуматься, то зачем городить всякие штуки вроде .htaccess?
    • 0
      Ну вот, например, я поддерживаю клиентский сайт, который написан неизвестно каким чертом. Если я проделаю эти простые действия — будет спокойнее как минимум. А может и спасти.
  • 0
    Красиво, и обязательно к использованию :)
  • –1
    А почему просто не допускать банальнейшую ошибку локального инклюда?
    При правильной архитектуре она исключена.
    • +1
      Тут не про локальный инклуд. А про загрузку файлов и исполнение их сервером как скриптов.
      • –2
        А нахрена это делать? о_О
        • 0
          чтоб сайт грохнуть, мало ли причин для загрузки вредоносного кода
  • +1
    Почему бы просто не пропускать php и тп скрипты?
  • 0
    <FilesMatch ".*">
    Order allow,deny
    Deny from all


    <FilesMatch "\.(список_легетимных_расширений через |)$|^$">
    Order deny,allow
    Allow from all


    Возможно, более верный вариант, незачем баловать «злоумышленников» даже выводом в формате html. А вообще, вернее проверять все на уровне скриптов- загрузчиков.
  • +1
    Обычная ситуация — на сервере находится локальный инклуд, находится компонент, позволяющий закачать данные на сервер(аплоад картинок, документов, аватарок и т.д.), локальным инклудом открывается эта картинка, аватар, в ней есть кусочек пхп кода, например в мета-полях картинки или тупо в конце, злоумышленник получает доступ, и никакой .htaccess тут не поможет.
    • 0
      Автор про Фому, вы про Ерёму.
  • 0
    В IIS в свойствах каталога ставится «Разрешать запуск: ничего».
    • 0
      А можно это как-то автоматизировать? Например, при установке CMS на хостинг.
      • 0
        может web.config?
  • 0
    На некоторых хостингах запрещены в целях безопасности все конфиги php из htaccess.
    • 0
      А еще на некоторых запрещены сокеты, system-like функции и т.д.
      Но может будем говорить про нормальные хостинги, а не про бесплатные, а?)
  • 0
    Все картинки и пр. сохранять на субдомен, на котором отключать выполнение скриптов на корню. Самый верный способ.
  • 0
    добавлю немного: recoilme.ru/blog/comments/237
  • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Никто не мешает в nginx добавить location, который будет блокировать обращение к определенным типам файлов по расширению или просто не передавать их на обработку php. Первый вариант:
      location ~ (^\dirname.*\.php$){
      deny all;
      }
      Это только для php, для остального несложно дополнить.
      А вопрос с неотдачей а обработку php решается просто более точным прописыванием того, что отдавать таки можно.
    • 0
      Про nginx. Обычно нужно разрешить лишь ограниченное число URL'ов, через которые вызывается PHP код (например, в Drupal только /index.php и, с localhost, /cron.php). Поэтому только их и передаём в PHP, остальное отдаём просто как статику.

      У меня есть несколько работающих примеров, можно посмотреть тут.
  • 0
    и Deny from all более универсально. имхо
    • 0
      * Files и Deny from all
  • 0
    Просто на заметку.

    Правило в htaccess:
    <Files ~ "\.(php[2-5]?|cgi|pl|fcgi|fpl|phtml|shtml|asp|jsp)$">
        Deny from all
    </Files>


    Или проверка при загрузке:
    <?php
    $filename = 'file.php';
    $pos = strrpos($filename, '.');
    $ext = ($pos !== false ? substr($filename, $pos + 1) : '');
    if (in_array($ext, array('php', '...'))) die('forbidden');
    ?>


    Это все не работает на файлах с двойным расширением file.php.txt (при чем последнее — не php), а они, между тем, на большинстве хостингов обрабатываются apache как скрипты.
    • –1
      а вы при заливке файлов на сервер расширенине не проверяете?
      • 0
        *всмысле имена файлов, на содержание «не хороших» слов.
    • +1
      pathinfo() отличная функция.
      • 0
        Спасибо, буду знать. А возвращает, кстати, rar =)

        <?php
        $r = pathinfo('/www/uploads/file.php.rar');
        print_r($r);
        ?>
        
        Array
        (
            [dirname] => /www/uploads
            [basename] => file.php.rar
            [extension] => rar
            [filename] => file.php
        )
  • 0
    Для своего блога давно такой .htaccess написал
  • 0
    Я правильно понимаю, что если на сайте нет каких-либо скриптов по закачке чего-либо, можно не беспокоиться?
    • 0
      Вообще, нет. :) Например при SQL-инъекции есть возможность записать в файл на диске любое содержимое. Ну и ваши скрипты могут тоже что-то писать на диск.
  • 0
    способ старый, но есть один минус когда используешь в качестве закачки файлов fckeditor (fckeditor.net), на некоторых серверах, по непонятным причинам fckeditor начинает ругаться на папку если в ней есть такой .htaccess и не закачивает файлы.
  • +3
    Ох. Опять велосипеды.

    Для этих целей есть RemoveType, кроме того, нужно отключить SSI в HTML, иначе я смогу включить любой файл или сделать exec. Потом надо запретить действие .htaccess для этого каталога (тут уже писали как). Может и ещё что-то упустил с утра (только проснулся, лёг поздно).

    В общем, ваш совет вредный.
    • +2
      Может статейку напишешь? Новички будут благодарны)
      • +1
        Пока занят по самое набалуйся. Может позже.
    • 0
      Ну, для скрипткидди совет точно вредный, не получится заюзать сплойт ;)
  • 0
    вот у меня с этим была беда в своё время.., ломали сайт, а я никак не могу понять как)))))
    следы все подтирали за собой… потом разобрался и закрыл дырку иенно таким способом!
  • +1
    имхо,
    отдавать контент скриптами. все, кто запрашивает недопустимые типы — получают

    header(«HTTP/1.1 403»);
    • 0
      На самом деле неважно что будет происходить — 403, 404 или редирект на смешарики.ру. Главное, чтобы не запустился.
      • 0
        но, тут можно и какие-то полезные операции делать.
  • –1
    В общем, как я понял, единственный *действительно* надежный способ — проверять расширение загружаемых файлов (например, только картинки).
    • 0
      Вы не правильно поняли что автор хотел сказать.
      Имеется ввиду то, что если сайт как-то взломают, то скрипты(шеллы и т.д) закачают в папку с правами 777 (например в папку с картинками).
      Можно через .htaccess запретить исполнения скриптов в этих папках
  • +1
    Из моего опыта.

    1. Все пользовательские файлы(аватарки, картинки, приложения) закачиваем, используя ftp функции. Таким образом нам удается полностью отказаться от прав 0777 и это будет работать везде.

    2. Директории кешей, а также smarty-вские templates_c тупо закрываем .htaccess deny from all

    3. Предложенный автором способ хорош, но не будет работать на некоторых хостингах с fast-cgi php.
  • +1
    При закачке меняю расширение на ожидаемое и все.
  • 0
    location ~ "^/([^\/]+|admincp/.+|modcp/.+)\.php$" {
    fastcgi_pass unix:/tmp/php-fcgi.socket;
    fastcgi_index index.php;
    fastcgi_param script_FILENAME /usr/local/www$fastcgi_script_name;
    include /etc/nginx/fastcgi_params;
    }
    location ~ \.php$ {
    internal;
    }

    Один из вариантов для nginx. Кусок конфига от форума с вбуллетином.
  • 0
    в принципе если речь только о заливке картинок то можно после заливки пересоздавать картинку через imagecopyresampled, но эт как дополнение к коду в htaccess`e.
  • 0
    После того, как через дырявый аплоадер старго фцкедитора залили спам рассыльщик и фишинг страницу в папке всех проектов с правами на запись всегда в корне есть файлик

    .htaccess

    RemoveHandler .php .phtml .php3
    AddType application/x-httpd-php-source .php .phtml .php3

    RemoveHandler .htm .html
    AddType application/x-httpd-php-source .htm .html
  • 0
    это лучше сделать сразу в httpd.conf (ну или vhosts.conf, кому как удобней) в секцию и добавить AllowOverride None. И тогда закачивай любой .htaccess — его апачь не будет признавать никак.
    • 0
      в секцию Directory, теги хабр убрал
  • 0
    А зачем вообще позволять закачивать файлы с такими расширениями? Обычно от юзера требуется jpg, png, gif или на крайний случай архив. Почему просто в самом скрипте закачки не поставить проверку на тип файла и не давать закачивать файлы с расширениями .html, .php, .phtml и т.д.?
    • 0
      Тут мы рассматриваем вариант, что скрипт закачки с возможными багами. :)
  • 0
    AddType «text/html» .php .cgi .pl .fcgi .fpl .phtml .shtml .php2 .php3 .php4 .php5 .asp .jsp
    А про XSS не забыли?

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