Правильного ответа нет. Любой выбор приводит к жертвам, любое решение требует платы. | Лишь навык имеет значение.
Хао.
Помогите глупенькому понять где проблема.
Есть веб-система. Пользователи грузят в неё файлы.
Файлы "как есть" пишутся с определённым пользователем и правами 0644
И есть доступ по FTP. Другой пользователь.
И надо, чтобы пользователь по FTP мог удалять файлы, созданные php-шным скриптом, который загружаемые файлы обрабатывает.
Что надо сделать?
В начале была мысль, после загрузки файла cmod'ом выставлять права 0666.
Из логики, что если есть права на запись, то можно удалить.
Нифига, 502 Permission denied (FileZilla).
Окей, чисто для эксперимента (ибо понимаю, что нельзя так) выставил 0777.
Отлично, файлик загрузился, FileZilla радостно сообщает, что у него 0777 права... но удалить не даёт.
Соответственно вопрос - что нужно делать с файлом после загрузки, чтобы потом можно было по ftp его удалять спокойно?
А то я чего-то туплю уже и/или вообще не въезжаю в механизм.
upd: Пришла мысль, что дело в правах папки, куда грузятся файлы. Окааай, выставил на неё 0666 скриптом.
То бишь, вроде как Чтение + Запись.
В итоге получил странный момент - FileZilla теперь вообще в эту папку войти не может.
Что вызывает у меня ещё большее непонимание. Если есть права на Чтение, почему нельзя прочитать папку и получить список её содержимого, что ftp-клиент сделать пытается?
Решено: Дело было всё же в правах на папки.
Не знаю, насколько это правильное решение, но пока оставился на следующем.
Т.к. после загрузки файла его нужно положить в определённое место, в зависимости от ряда параметров, путь к файлу "собирается" по кусочкам.
Исходя из этого, решение следующее - на каждом шаге сборки итогового адреса файла, очередному "звену" цепочки присваиваются права 0777.
Когда адрес собран целиком (то бишь, в конце собсно само имя файла) - файлу присваивается 0666.
В итоге, можно по ftp спокойно удалять всё что необходимо, файл доступен по прямой ссылке в браузере, но не является исполняемым.
Пока так. Если что можно/нужно сделать иначе, буду рад консультации.)
Помогите глупенькому понять где проблема.
Есть веб-система. Пользователи грузят в неё файлы.
Файлы "как есть" пишутся с определённым пользователем и правами 0644
И есть доступ по FTP. Другой пользователь.
И надо, чтобы пользователь по FTP мог удалять файлы, созданные php-шным скриптом, который загружаемые файлы обрабатывает.
Что надо сделать?
В начале была мысль, после загрузки файла cmod'ом выставлять права 0666.
Из логики, что если есть права на запись, то можно удалить.
Нифига, 502 Permission denied (FileZilla).
Окей, чисто для эксперимента (ибо понимаю, что нельзя так) выставил 0777.
Отлично, файлик загрузился, FileZilla радостно сообщает, что у него 0777 права... но удалить не даёт.
Соответственно вопрос - что нужно делать с файлом после загрузки, чтобы потом можно было по ftp его удалять спокойно?
А то я чего-то туплю уже и/или вообще не въезжаю в механизм.
upd: Пришла мысль, что дело в правах папки, куда грузятся файлы. Окааай, выставил на неё 0666 скриптом.
То бишь, вроде как Чтение + Запись.
В итоге получил странный момент - FileZilla теперь вообще в эту папку войти не может.
Что вызывает у меня ещё большее непонимание. Если есть права на Чтение, почему нельзя прочитать папку и получить список её содержимого, что ftp-клиент сделать пытается?
Решено: Дело было всё же в правах на папки.
Не знаю, насколько это правильное решение, но пока оставился на следующем.
Т.к. после загрузки файла его нужно положить в определённое место, в зависимости от ряда параметров, путь к файлу "собирается" по кусочкам.
Исходя из этого, решение следующее - на каждом шаге сборки итогового адреса файла, очередному "звену" цепочки присваиваются права 0777.
Когда адрес собран целиком (то бишь, в конце собсно само имя файла) - файлу присваивается 0666.
В итоге, можно по ftp спокойно удалять всё что необходимо, файл доступен по прямой ссылке в браузере, но не является исполняемым.
Пока так. Если что можно/нужно сделать иначе, буду рад консультации.)
В итоге получил странный момент - FileZilla теперь вообще в эту папку войти не может.
Потому что чтобы зайти в директорию, нужен бит x. Он же (помимо w) нужен для создания/удаления/переименования файлов в ней.
Читайте маны, там все есть.
man chmod, chown, chattr, acl, setfacl.
рекурсивный 777 начиная непонятно откуда? Обколюцца своей марихуаной...
Эт я уже понял, меня это и смутило.
рекурсивный 777 начиная непонятно откуда? Обколюцца своей марихуаной...
Не-не-не, нифига не рекурсивный.
Вот загружается файл, с ним прилетают данные всякие, на основании которых строится путь, где должен файл лежать.
$path = $_POST["target"];
#target
Смотрим, есть ли папка.
Если нет - создаём её и даём ей права 0777 (чтобы могли заходить, писать в неё и читать из неё).
$path = $path . "/" . $_POST["category"];
#target/category
Аналогично предыдущему - если есть, идём дальше. Если нет - создаём папку с именем type и назначаем 0777. Для тех же целей.
Повторяем дальше по цепочке, на основании данных.
В итоге имеем
$path = target/category/type/num
это путь, где будет файл лежать. target, category, type, num - имеют права 0777. Иначе в них не залезем, не прочитаем и не запишем.
А дальше идёт
$path = $path . "/" . $file_name;
Собсно сам файл
target/category/type/num/file_name
И вот он уже имеет 0666, чтобы его можно было читать и можно было удалить.
Такой процесс. Если где-то здесь туплю - поправляйте, объясните почему нельзя так делать.
Ни о каком рекурсивном 0777 речи не было.
$path = $path . "/" . $_POST[""]
$path = $path . "/" . $_POST[""]
$path = $path . "/" . $_POST[""]
$path = $path . "/" . $_POST[""]
$path = $path . "/" . $_POST[""]
$path = $path . "/" . $_POST[""]
$path = $path . "/" . $_POST[""]
$path = $path . "/" . $_POST[""]
На всякий случай: ещё стоит учесть, что присланное имя файла напрямую использовать нельзя. Например, там может быть ../../.. Это относится и к любым другим данным от пользователя. К примеру, $_POST['category'], как в данном случае.
Также необходимо, чтобы через настройки сервера (можно .htaccess, если это апач) каталог, куда грузятся файлы был закрыт для исполнения скриптов, иначе туда загрузят .php.
И вот он уже имеет 0666, чтобы его можно было читать и можно было удалить.
Чтобы удалить, права должны быть у папки. У самого файла эти права нужны только для того, чтобы его изменять. Если изменять файл без удаления не нужно, можно смело ставить 444.
Ну, на самом деле, пользователь ничего не указывает сам, из того что в пути файла используется.
По сути это input type="hidden"ы value которых генерится php на стадии генерации странички, с которой пользователь файлы грузит.
То бишь, если он специально не полезет в код менять значения этих скрытых полей, проблем не будет.
* А если полезет, то проблемы будут в любом случае, т.к. лезть будет везде где сможет.
Если изменять файл без удаления не нужно, можно смело ставить 444.
О, а это мысль. В принципе да, файл-то уже записался, а т.к. он сугубо для скачивания, то можно чисто чтение выставить.
Спасибо, пойду подкорректирую сейчас.
Также необходимо, чтобы через настройки сервера (можно .htaccess, если это апач) каталог, куда грузятся файлы был закрыт для исполнения скриптов, иначе туда загрузят .php.
Тут как бы, посмотрите выше - я не знаю заранее, какой будет каталог. Он генерится в зависимости от данных.
Ну, кроме target - этот я ручками создаю, т.к. в нём ещё всякие .ini лежат, которыми я систему конфигурирую.
То бишь, это получается для каждого созданного target'а делать запись в .htaccess?
Вообще, так ли надо?
Во-первых, файл (ну, расширение, имя файла то бишь) проверяется на клиенте. Если недопустимое расширение - идёт лесом и не даёт форму отправить.
Опять же, если кому сильно понадобится, он может влезть ручками или подменить скрипт, чтобы проверку убрать... но это уже из серии "к нам полезли и они любопытны". То бишь, не массовый случай, а исключительный.
Во-вторых, если я автоматом 444 ставлю, то что этот .php может сделать собсно?
Я так понимаю, что он может вывести внутренний html и не более.
Ведь даже банальное echo потребует чтобы файл был исполняемым. Или я не правильно понимаю?
upd: Насчёт "не использовать прямо". Оно не используется. Как минимум, всё приводится в нижний регистр и в транслит.
Ну и в целом, операционки ж вроде не дают делать папки с именем вида ".", "..", "./" и т.д.
А если и дадут - максимум что из этого выйдет - пользователь не сможет получить свой файл, т.к. его перекинет куда-нибудь, а оттуда перекинет на главную, т.к. пришёл "с пустыми руками", без нужных данных. И тогда - сам дурак, не сможешь своим файликом воспользоваться и получишь втык от других людей, которые должны с ним работать.
Тут дело не в этом. Дело в том, что таким образом можно записать в произвольный каталог (куда права на запись у веб-сервера будут, конечно).
я не знаю заранее, какой будет каталог. Он генерится в зависимости от данных.
Если оно пришло из POST, то не только пользователь, но и сторонний злоумышленник может заставить пользователя передать туда всё, что угодно (например, страничка с формой и submit'ом скриптом на другой домен при заходе на неё). Соответственно, опять получаем запись в произвольный каталог.
Ведь даже банальное echo потребует чтобы файл был исполняемым.
Обычно нет. Права на исполнение нужны для запуска файлов просто по имени. Скрипты, как правило, интерпретируются без необходимости ставить +x.
То бишь, это получается для каждого созданного target'а делать запись в .htaccess?
Скорее там будет что-то типа files/ или uploads/ и вот на него целиком.
Опять же, если кому сильно понадобится, он может влезть ручками или подменить скрипт
Через FTP - да, но не через веб-интерфейс. Я так понимаю, что веб-интерфейсом будут пользоваться не только те, у кого есть доступ по FTP.
То бишь, если он специально не полезет в код менять значения этих скрытых полей, проблем не будет.* А если полезет, то проблемы будут в любом случае, т.к. лезть будет везде где сможет.
Достаточно это сделать в браузере, доступа к исходному коду не нужно. Или, как я уже писал выше, не обязательно, чтобы он сам у себя в браузере что-то делал. Достаточно дать ему ссылочку на «заряженную» страничку вообще на стороннем сайте. Если, конечно, нет CSRF-защиты.
Даже по ftp - не пускает и всё.
Во-вторых, нужно ещё как-то догадаться, какие из пересылаемых данных участвуют в составлении пути файла, а какие нет.
В-третьих, ну запишет пользователь куда не надо - сам же не сможет потом с этим файлом работать, как и другие люди, кто должен файлы обрабатывать.
Получит втык от них. Тем более, что файлу 444 выставляется в любом случае. То бишь он не исполняемый.
В чём тогда проблема?
Обычно нет. Права на исполнение нужны для запуска файлов просто по имени. Скрипты, как правило, интерпретируются без необходимости ставить +x.
Эм... то бишь, всё что я пишу внутри .php будет работать в любом случае, даже если прав на исполнение нет?
Просто у меня были моменты, когда забывал выставить право запуска и у меня скрипт просто не работал - белый экран и всё. Потому и уточняю.
Через FTP - да, но не через веб-интерфейс. Я так понимаю, что веб-интерфейсом будут пользоваться не только те, у кого есть доступ по FTP.
Соррян, не уточнил - тут речь о клиентских скриптах, js то бишь.
Я про то, что если кто-то захочет сильно - он и форму в html подменит, и js. И оно как бы в любом случае так (разве что обфускацию с минимизацией делать, что мне не очень нравится просто само по себе).
Иначе говоря - если кто-то захочет подменить входные данные - он их подменит.
И тогда можно будет какой-нибудь .php загрузить на сервер (расширение на клиенте проверяется просто).
Только если он из-за 444 не сможет его выполнить, то в чём угроза/проблема?
Конечно. Если кто-то только специально не замутил что-нибудь хитрое в настройках сервера, то и PHP и, в принципе, любым другим интерепретаторам совершенно наплевать на наличие execute-бита. А если всё же и есть какой-нибудь костыль для точки входа, то include/require уж точно ничего не проверяют.
В-третьих, ну запишет пользователь куда не надо - сам же не сможет потом с этим файлом работать
Он может перезаписать тот же самый условный index.php, через который он это и загружает, если прав хватит. Или файл, подключаемый через include/require.
Соррян, не уточнил - тут речь о клиентских скриптах, js то бишь.
Ну поэтому такие проверки делаются на сервере, а на JS могут дублироваться только для уменьшения числа некорректных обращений к серверу для его разгрузки. Просто как оптимизация в случаях, где нет злого умысла.
1) Сделать дублирующую проверку расширений файлов на сервере. Если расширения нет - иди лесом. Если расширение не разрешено - иди лесом.
Что ещё? Или этого хватит?
Далее. Какой-то единой папки типа "upload" или "files" нет. То бишь, я не знаю, какое имя будет у первого каталога в цепочке, но знаю какая будет его внутренняя структура и где он сам будет лежать.
В системе, чтобы не делать дубли одних и тех же скриптов на каждый чих, я сделал универсальность. Один модуль - одна задача.
А контекст определяется get-параметрами. Задашь один набор - будешь в одном контексте работать, задашь другой - будешь в другом.
Но сам скрипт, выполняющий работу, всегда один.
Т.к. я не знаю в каком контексте пользователь работает в данный момент, я передаю контекст в скрытом поле при заливке файла.
И он используется при генерации пути. Чтобы файлы лежали отдельно каждый по своему контексту.
Что я могу - я могу сделать доп. проверку контекста по БД - там есть табличка, с описанием контекстов и прочих сервисных данных.
Например, есть у меня в табличке
context_one
context_two
context_three
Прилетает загружаемый файл, который содержит данные о контексте, категориях, типах и т.д.
Контекст становится первым звеном в пути файла, куда он скопируется после загрузки.
context_one/category/num/file_name Типа такого.
Я могу сделать проверку контекста. Типа, если пришёл context_five, которого у меня в БД нет - иди лесом.
Вопрос - есть ли в этом смысл?
Просто если совсем параноить, то это ж можно ещё и другие добавить
- "зареган ли такой пользователь?"
- "включён ли он?" (у меня просто отдельное поле acitve, которое по дефолту в 0, а 1 становится только после подтверждения по ссылке)
- "есть ли в конфигурационном .ini присланная category?"
- "есть ли такой num?"
и т.д. Что добавит на каждую загрузку файлов ещё десяток обращений к БД и доп. парсинг конфигов.
Эт если вообще все присланные данные проверять.
Что в итоге несколько добавит монструозности, как мне кажется, и естессно увеличит объём обрабатывающего кода.
Я могу это сделать, вопрос в том, имеет ли это смысл?
Система сама по себе не то чтобы большая. Пользователей обычно до 3-4к зареганных, при этом в период активности одновременно работает человек 200-300.
В особые пики может добавиться +100-200 человек. Но это вообще должны звёзды сложиться особо, ибо практически всегда люди не одновременно работают, а "размазаны" по времени. В целом система, так чтобы работала и в неё ходили, фурычит 3-4 месяца в году, в остальное время просто лежит архивом данных, с которым работает от силы человек 10-15.
По специфике - сугубо для определённых задач без какой-либо коммерчесской составляющей.
Грубо говоря, самое важное что можно скомуниздить - набор e-mail'ов, фио, номеров телефонов и ссылок на вк (их, в среднем, от 5 до 30 на каждого зареганного пользователя). Остальная инфа стороннему человеку будет просто бесполезной и не о чём, т.к. касается специфики для которой система разрабатывалась и надо знать что с этой инфой делать, как её сопоставлять и ещё много чего, чтобы бессмысленный набор текста превратился в что-то, что может быть полезным кому-то вне системы. И то, это надо постараться ещё, специфика такая.
В общем, практически всё внутри системы бесполезно для тех, кто в ней не работает, кто не связан с её спецификой.
Польза (и то, сомнительная) может быть только от набора контактных данных, не более.
И вот, с учётом того что "нафих мы кому сдались" и людей у нас немного, есть ли вообще смысл городить что-то ещё, кроме серверной проверки расширений?
Во-первых, список-то их где-то есть и можно проверить соответствие чему-либо из списка.
Во-вторых, можно пойти наоборот - не запрещать выполнять скрипты где-то в одном месте, а разрешить только в одном, которое будет закрыто на запись, и которое можно дополнительно проверить при желании.
Просто если совсем параноить, то это ж можно ещё и другие добавить - "зареган ли такой пользователь?"- "включён ли он?" (у меня просто отдельное поле acitve, которое по дефолту в 0, а 1 становится только после подтверждения по ссылке)- "есть ли в конфигурационном .ini присланная category?"- "есть ли такой num?"
Обычно так все и делают.
есть ли вообще смысл городить что-то ещё, кроме серверной проверки расширений
Может быть и нет. Но, по крайней мере, лучше всё-таки представлять, где есть уязвимые точки и что через них можно сделать.
В описанном сейчас варианте возможна загрузка произвольных файлов в произвольное место, в т.ч. скриптов. Это я считаю серьёзной уязвимостью даже при отсутствии каких-либо важных данных в системе, т.к. позволяет злоумышленнику использовать ваш сервер в своих целях. То есть хотя бы проверка на расширение файла на сервере нужна. Остальное уже не так страшно.
Ну, это я сделал.)))