Обновить

Как не стать героем истории «программа удалила мне 800 ГБ»: безопасное рекурсивное удаление в Bash и Python

Время на прочтение6 мин
Охват и читатели8K
Всего голосов 4: ↑3 и ↓1+3
Комментарии2

Комментарии 2

У safe_rmtree остаётся гонка на промежуточных каталогах, даже при shutil.rmtree.avoids_symlink_attacks=True. Проверил на Linux с Python 3.15: после проверок обёртки, непосредственно перед rmtree(real), отдельный процесс переименовал allowed/parent и заменил его симлинком на foreign — в результате удалился foreign/cache/important.txt, а исходный allowed/parent/cache остался в переименованном каталоге. Защита rmtree от симлинков не закрепляет предков переданного абсолютного пути. Если другой процесс может менять эти каталоги, для такой обёртки нужно либо исключить эту возможность правами доступа, либо открывать путь от доверенного дескриптора по компонентам с запретом симлинков и передавать rmtree имя последнего компонента с dir_fd открытого родителя.

Спасибо, точное замечание. Обёртка в статье защищает от ошибок в самом скрипте: пустой переменной, не того пути, симлинка внутри дерева. От другого процесса, который в этот момент может менять родительские каталоги, она не защищает, и avoids_symlink_attacks тут действительно не помогает: он касается только обхода внутри удаляемого дерева, а не предков пути. Если такой процесс возможен, правильнее идти от доверенного дескриптора: открывать компоненты пути по одному через os.open(name, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=parent_fd) и вызывать shutil.rmtree(last_name, dir_fd=parent_fd) (параметр dir_fd есть с Python 3.11). Либо, как вы пишете, закрыть права на запись в эти каталоги. Добавлю это оговоркой в статью.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации