Почему совет «поставь chmod 777, чтобы заработало» ломает сервер
Сайт падает с «Permission denied», деплой ломается на записи в кэш, WordPress не может обновить плагин — и первый совет, который находится в интернете за тридцать секунд, звучит одинаково: «сделай chmod -R 777 на папку, и всё заработает». Иногда действительно заработает. Проблема в том, что вы при этом не чините сервер, а открываете дверь настежь и вешаете на неё табличку «решено» — и рано или поздно это аукается совсем не тем способом, каким вы ждали.
Содержание
Почему этот совет так живуч
chmod 777 работает почти всегда — в этом его главная опасность. Когда ядро проверяет, может ли процесс записать файл, оно смотрит на биты прав и на то, какому из трёх классов — владелец, группа, остальные — принадлежит запрашивающий процесс. Если открыть запись всем трём классам сразу, вопрос «а точно ли у процесса есть право» просто перестаёт быть узким местом: кто угодно и с любыми правами получает доступ. Ошибка исчезает с экрана, деплой проходит, плагин обновляется — и на этом для многих разбор проблемы заканчивается, потому что цель («заработало») формально достигнута.
Совет тиражируется в тысячах ответов на форумах и в комментариях к урокам именно потому, что он работает быстро и не требует понимания, кто на самом деле должен владеть файлом. На локальной машине разработчика, где единственный пользователь — он сам, а процессы веб-сервера тоже часто запущены от его же аккаунта, такая правка почти безобидна: в этой системе нет других учётных записей, которые могли бы воспользоваться открытой записью. На боевом сервере, тем более на VPS с несколькими сайтами или процессами, ситуация принципиально другая — и об этом ниже.
Что на самом деле означают три семёрки
Права доступа в Unix-подобных системах — это три группы по три бита: чтение, запись, выполнение — отдельно для владельца файла, отдельно для группы-владельца и отдельно для «остальных» (other). 777 в восьмеричной записи — это rwxrwxrwx: полный доступ каждому из трёх классов, включая последний. А «остальные» — это буквально все учётные записи в системе, кроме владельца и участников группы-владельца: другой пользователь, демон с собственным системным UID, процесс любого другого сайта на этом же сервере, скомпрометированный скрипт кого угодно, у кого вообще есть shell или cron-задача на этой машине.
Если хотите разобраться в механике на уровне ядра — как именно происходит проверка при каждом системном вызове и почему даже 777 иногда не спасает (например, из-за прав на родительский каталог), это подробно разобрано в статье как ядро проверяет права и почему chmod 777 не помог. А то, какие права получает файл при создании и почему они не совпадают с ожиданиями, объясняется в материале про механику umask.
Ключевая ошибка в голове у того, кто ставит 777 — воспринимать права как техническую формальность, мешающую работе, а не как границу доверия между процессами и пользователями одной машины. Открывая запись «остальным», вы не чините процесс, у которого не было доступа, а даёте доступ вообще всем процессам, включая те, у которых доступа не должно быть никогда.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверРиск №1: сервер вы делите не только с собой
На VPS с несколькими сайтами или сервисами, за исключением совсем небольших однопользовательских установок, файлы разных проектов часто обслуживаются разными системными пользователями — отдельный пул PHP-FPM с user = site1 для одного сайта, user = site2 для другого, отдельные системные аккаунты под каждое Docker-приложение или крон-задачу. Это разделение существует специально: если один сайт скомпрометируют через уязвимый плагин или устаревшую библиотеку, злоумышленник получает шелл или права записи только в рамках процесса именно этого сайта — то есть только там, куда есть доступ у его пользователя.
chmod -R 777 на каталог одного проекта полностью обнуляет эту изоляцию для этого каталога: теперь в него может писать процесс любого другого сайта на этой же машине, любой cron-скрипт, запущенный от другого пользователя, любой временный контейнер, которому по ошибке или по злому умыслу дали доступ к файловой системе хоста. На практике это означает: если у соседнего сайта на том же сервере найдут дыру раньше, чем у вас, следующей жертвой станете вы — просто потому что оставили для этого открытую дверь на своей территории. Подробнее о том, чем в принципе отличается модель изоляции VPS от общего хостинга, разобрано в статье общий хостинг против VPS — там же видно, почему на shared-тарифах разработчики хостингов особенно жёстко следят за такими правами: там плотность чужих процессов на одной машине ещё выше.
Отдельная и частая ситуация — рекурсивный chmod -R 777 на весь /var/www «чтобы уж наверняка сработало». Это не только открывает все сайты сразу, но и стирает разницу в правах, которая раньше отделяла один проект от другого: если раньше у сайта А не было доступа к файлам сайта Б на файловой системе, то после такой команды они читают и пишут друг в друга свободно.
Риск №2: софт умеет сам отказываться работать с такими правами
Часть системного и серверного ПО написана с расчётом именно на такую атаку и явно проверяет права перед тем, как довериться файлу — так что 777 иногда не просто небезопасен, а сразу и не решает исходную проблему. Несколько показательных примеров, которые проверяются десятилетиями и хорошо задокументированы:
- OpenSSH отказывается использовать приватный ключ или домашний каталог пользователя, если у них есть права на запись для группы или для остальных — в логе появляется что-то вида
Authentication refused: bad ownership or modes for directory /home/user. Дать ключу или~/.sshправа777не «почините» доступ, а прямо заблокируете вход по этому ключу. - sudo требует, чтобы
/etc/sudoersи файлы в/etc/sudoers.d/имели строго0440—visudoоткажется сохранять файл с более широкими правами, а самsudoможет вовсе отказаться запускаться, если файл конфигурации оказался world-writable. - Apache suEXEC — механизм, который разрешает CGI/PHP-скриптам выполняться от имени владельца сайта, а не от общего пользователя веб-сервера, — специально проверяет, что скрипт и содержащий его каталог не доступны на запись группе или всем. Если это условие нарушено, suEXEC откажется выполнять скрипт и запишет в лог отказ по правам, а не выполнит его «раз уж прав теперь много».
Логика здесь одна и та же в разных программах: world-writable файл конфигурации или исполняемый файл — это признак того, что его мог подменить кто угодно, поэтому доверенный процесс, особенно запущенный с повышенными привилегиями, скорее откажется его использовать, чем выполнит потенциально чужой код. Получается парадокс, с которым сталкивается немало новичков: после chmod 777 ошибка доступа меняется на другую, ещё менее понятную — и время на диагностику не сокращается, а растёт, потому что теперь непонятно, почему «права же теперь максимальные, а всё равно не работает».
Как найти реального виновника, а не право
Правильный порядок действий — не подбирать права методом «дай побольше», а сначала выяснить, от чьего имени процесс пытается прочитать или записать файл, и сравнить это с текущим владельцем.
Узнать, от какого пользователя реально работает веб-сервер и PHP:
ps aux | grep -E 'nginx|apache2|php-fpm'
grep -E '^\s*user' /etc/nginx/nginx.conf
grep -E '^\s*user\s*=' /etc/php/8.3/fpm/pool.d/www.conf
Обычно это www-data в Debian/Ubuntu, nginx или apache в RHEL-подобных дистрибутивах, либо отдельный пользователь на VPS с несколькими сайтами. Дальше смотрим, кому реально принадлежит проблемный файл:
stat -c '%U:%G %a %n' /var/www/site/wp-content/uploads
Если владелец — root или, например, ваш SSH-пользователь deploy, а веб-сервер работает от www-data, то вот она, настоящая причина: не «прав мало», а «файл принадлежит не тому». Правка одной командой:
sudo chown -R www-data:www-data /var/www/site
Это единственное реальное исправление в 90% случаев, когда Permission denied вылезает при загрузке файлов через WordPress, при записи кэша фреймворком или при деплое через CI. Проверить логи процесса до правки прав тоже стоит — иногда ошибка вообще не про файловые права, а про SELinux/AppArmor-контекст или про то, что каталог для записи не существует и его нужно сперва создать с правильным владельцем:
sudo journalctl -u php8.3-fpm --since "10 min ago" | grep -i denied
sudo tail -n 50 /var/log/nginx/error.log
Минимально достаточные права вместо 777
После того как владелец файлов приведён в порядок, обычно хватает стандартной, узкой схемы прав — без единой семёрки в третьей позиции:
| Объект | Права | Владелец:группа | Зачем именно так |
|---|---|---|---|
| Каталоги сайта | 755 | www-data:www-data | владельцу — полный доступ, остальным — только чтение и вход в каталог |
| PHP/HTML/JS-файлы | 644 | www-data:www-data | веб-серверу и владельцу — чтение и запись владельцу, всем прочим — только чтение |
| Каталог для загрузок (uploads, cache) | 775 | www-data:www-data | запись владельцу и группе, если процессов записи несколько — но не «всем» |
.env, конфиги с паролями/токенами | 600 | www-data:www-data (или владелец приложения) | читает только владелец процесса, никто больше |
| Приватные SSH-ключи | 600 | пользователь-владелец | иначе sshd откажется их использовать вовсе |
Если запись в один каталог нужна сразу нескольким процессам с разными системными пользователями (например, деплой-скрипту от deploy и веб-серверу от www-data), решение — не 777, а общая группа и setgid-бит, чтобы новые файлы автоматически наследовали нужную группу:
sudo groupadd webdeploy
sudo usermod -aG webdeploy www-data
sudo usermod -aG webdeploy deploy
sudo chgrp -R webdeploy /var/www/site/wp-content/uploads
sudo chmod -R 2775 /var/www/site/wp-content/uploads
Цифра 2 перед 775 — это setgid-бит: новые файлы и подкаталоги, создаваемые внутри, будут наследовать группу webdeploy, а не группу того, кто их создал. Если процессов и пользователей больше двух и общая группа становится неудобной, точнее работают ACL — они позволяют выдать конкретному пользователю или сервису доступ к конкретному каталогу, не трогая остальных:
sudo setfacl -R -m u:deploy:rwx /var/www/site/releases
sudo setfacl -d -m u:deploy:rwx /var/www/site/releases
Вторая команда задаёт права по умолчанию (-d) — они автоматически применятся ко всем новым файлам и подкаталогам внутри, без необходимости перевыставлять права после каждого деплоя. Как именно новые файлы получают свои изначальные права и при чём тут umask, если результат вас удивляет — отдельно разобрано в статье про механику umask при создании файлов. Общий чек-лист базовой защиты нового сервера, куда права доступа входят одним из пунктов наравне с SSH-ключами и фаерволом, есть в материале чек-лист безопасности нового сервера.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
А если chown невозможен — например, файл создаётся временно контейнером с другим UID?
Тогда решение — не 777 на хосте, а согласовать UID/GID контейнера с UID/GID процесса на хосте (директива user в docker-compose.yml) или пробросить том с явным :Z/:z для SELinux-меток, а не открывать каталог на запись всем.
Можно ли временно поставить 777 просто для диагностики, а потом вернуть обратно?
Формально да, но на боевом сервере даже кратковременное окно — это риск, а диагностика через stat и логи процесса (кто владелец, от кого работает сервис) занимает не больше времени и не оставляет открытой дыры, если вы забудете вернуть права обратно — что на практике случается регулярно.
Почему у меня после chmod -R 777 сайт всё равно не работает?
Скорее всего проблема не в правах файла, а в правах родительского каталога, в SELinux/AppArmor-контексте, в отсутствии самого каталога или в том, что процесс, который вы «чините», физически не тот, что читает ошибку — например, ошибку пишет PHP-FPM, а вы правите права от имени nginx.
Чем плохи права 775 или 755, если ими тоже иногда злоупотребляют?
Ничем, если владелец и группа выставлены верно — они и так не дают доступ «всем», и то, что группа-владелец действительно объединяет только нужные процессы, а не root, не соседние сайты и не случайных пользователей системы.
Как найти файлы с опасными правами на уже работающем сервере?
find /var/www -type f -perm -o+w покажет все world-writable файлы, find /var/www -type d -perm -o+w — каталоги; дальше для каждого стоит вручную выяснить владельца процесса и вернуть узкие права по таблице выше.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →