Учитывая приведенный ниже сценарий, как я могу убедиться, что аргумент содержит только допустимое имя файла, /home/charlesingalls/
а не путь ( ../home/carolineingalls/
), подстановочный знак и т. Д.?
Я только хочу, чтобы скрипт мог удалить один файл из заданного жестко закодированного каталога. Этот скрипт будет запускаться как привилегированный пользователь.
#!/bin/bash
rm -f /home/charlesingalls/"$1"
/
. Подстановочные знаки не интерпретируются внутри кавычек.-r
сrm
.rm -r
предназначен для рекурсивного удаления каталога и всех файлов и каталогов под ним. Это полезно только при удалении каталогов. В общем, не груз-культ. т.е. не просто копируйте вещи, которые выглядят полезными, в вашу командную строку или скрипт, не понимая, что они делают или как они работают. Самолеты богов, несущие волшебный груз, могут разозлиться и удалить все свои файлы.Ответы:
Если вы хотите удалить только файл
/home/charlesingalls
(а не файл в подкаталоге), тогда это просто: просто убедитесь, что аргумент не содержит a/
.Это работает,
rm
даже если аргумент равен.
или..
или пуст, но в этом случаеrm
безуспешно не удастся удалить каталог.Подстановочные знаки здесь не имеют значения, поскольку расширение подстановочных знаков не выполняется.
Это безопасно даже при наличии символических ссылок: если файл является символической ссылкой, символическая ссылка (которая находится в
/home/charlesingalls
) удаляется, и цель этой ссылки не затрагивается.Обратите внимание, что это предполагает, что
/home/charlesingalls
не может быть перемещен или изменен. Это должно быть хорошо, если каталог жестко запрограммирован в сценарии, но если он определен по переменным, то определение может перестать быть действительным ко времени выполненияrm
команды.Основываясь на дополнительной информации о том, что аргумент является именем виртуального хоста, вы должны сделать белый, а не черный список: убедитесь, что имя является разумным именем виртуального хоста, а не просто запрещает косую черту. Я проверяю, что имя начинается со строчной буквы или цифры и что оно не содержит символов, кроме строчных букв, цифр, точек и тире.
источник
.
Этот ответ предполагает, что
$1
разрешено включать подкаталоги. Если вас интересует более простой случай, когда$1
должно быть простое имя каталога, посмотрите другой ответ.Подстановочные знаки не раскрываются в двойных кавычках. Поскольку
$1
в двойных кавычках, подстановочные знаки не являются проблемой.Обе
../
и символические ссылки могут скрыть реальное местоположение файла. Ниже показаны тесты, позволяющие определить, находится ли файл действительно, а не просто так, как мы хотим.Новые системы: использование
realpath
Что касается того, чтобы узнать, действительно ли файл находится в самом деле, находится ли он под
/home/charlesingalls/
или нет, вы можете использоватьrealpath
:Вышеприведенное запускается,
exit 1
если указанный файл$1
находится где-либо, кроме каталога/home/charlesingalls/
.realpath
канонизирует весь путь, устраняя как символические ссылки, так и../
.realpath
является частью GNU coreutils и должен быть доступен в любой системе Linux.realpath
требуется GNU coreutils 8.15 (январь 2012 г.) или выше .Примеры
Чтобы продемонстрировать, как realpath следует
../
для определения реального местоположения файла (например,-q
опция grep опущена, так что фактический вывод grep видим):Чтобы продемонстрировать, как следует символические ссылки:
Старые системы: использование
readlink -e
readlink
также способен кононизировать путь, следуя как символическим ссылкам, так и../
:Используя те же файлы примеров:
В дополнение к тому, что они доступны в более старых системах GNU, версии
readlink
доступны в BSD.источник
coreutils
не имеетrealpath
-f
(«все компоненты должны существовать, кроме последнего») и в примерах используется-e
(«все компоненты должны существовать»), что немного сбивает с толку.rm
действует сам аргумент, а не его цель.grep -q
чтобы неgrep
выводить совпадающие строки. Вы по-прежнему получаете статус выхода,&&
и||
все равно работаете так, как привыкли.Если вы хотите полностью запретить пути, самый простой способ - проверить, содержит ли переменная слеш (
/
). В Баш:Это заблокирует все пути, в том числе
foo/bar
. Вы можете проверить..
вместо этого, но это оставит возможность символических ссылок, указывающих на каталоги вне целевого пути.Если вы хотите разрешить удаление только одного файла, я не думаю, что вы должны использовать
rm -r
.Кроме того, в зависимости от того, что вы делаете, вы можете использовать системные права доступа к файлам, чтобы разрешить удаление только тех файлов, которые пользователь может удалить самостоятельно. Что-то вроде этого:
Хотя, как прокомментировал @Gilles, здесь есть проблема с кавычками: он потерпит неудачу, если будет
$1
содержать одинарную кавычку, поэтому сначала следует проверить переменную (напримерif [[ "$1" = *\'* ]] ; then fail...
, с помощью белого списка разумного набора символов) или передать имя файла через него. переменная окружения с, например,источник
su
команда не работает, потому что цитирование неверно. Вы выполняете команду с аргументом, интерполированным как фрагмент оболочки. Например, если аргумент равен,$(touch foo)
тогда ваш код выполняетсяtouch foo
.'
на'\''
), либо пропустить ее через другой канал, такой как переменная окружения (что я и сделал бы здесь:)file_to_remove="$1" su -c 'rm "/home/charlesingalls/$file_to_remove"'
. Получается, что имя файла должно быть именем виртуального хоста, поэтому здесь также можно отказаться от всех специальных символов.