In-Band: экранирование произвольных данных в контексте без кавычек
Не делай этого. См. Раздел «Out-of-Band» ниже.
Чтобы произвольная строка C (не содержащая NUL) оценивалась сама по себе при использовании в контексте без кавычек в строго POSIX-совместимой оболочке, вы можете использовать следующие шаги:
- Добавить
'
(переход от необходимого начального без кавычек контекста к контексту в одинарных кавычках).
- Заменить каждый литерал
'
в данных строкой '"'"'
. Эти персонажи работают следующим образом:
'
закрывает начальный контекст в одинарных кавычках.
"
входит в двойные кавычки.
'
в двойных кавычках является литералом.
"
закрывает контекст в двойных кавычках.
'
повторно входит в одинарные кавычки.
- Добавить
'
(возврат к необходимому начальному контексту в одинарных кавычках).
Это работает правильно в POSIX-совместимой оболочке, потому что единственный символ, который не является литералом внутри контекста в одинарных кавычках, это '
; в этом контексте даже обратные слеши считаются буквальными.
Тем не менее, это работает правильно только тогда, когда сигилы используются только в не заключенном в кавычки контексте (таким образом, на ваших пользователей возлагается бремя, чтобы все было правильно), и когда оболочка строго POSIX-совместима. Кроме того, в худшем случае строка, сгенерированная этим преобразованием, может быть в 5 раз длиннее исходной; таким образом, нужно быть осторожным с распределением памяти, используемой для преобразования.
(Можно спросить, почему вместо '\''
рекомендуется '"'"'
; это потому, что обратные слеши меняют свое значение, используемое в устаревшем синтаксисе подстановки команд backtick, поэтому более длинная форма более устойчива).
Out-of-Band: переменные среды или аргументы командной строки
Данные должны передаваться только вне диапазона из кода, чтобы они вообще никогда не проходили через анализатор. При вызове оболочки есть два простых способа сделать это (кроме использования файлов): переменные среды и аргументы командной строки.
В обоих нижеприведенных механизмах нужно доверять только user_provided_shell_script
(хотя для этого также необходимо, чтобы ему доверяли не вносить новые или дополнительные уязвимости; использование eval
или любого другого морального эквивалента аннулирует все гарантии, но это проблема пользователя, а не ваша).
Использование переменных среды
Исключая обработку ошибок (если setenv()
возвращает ненулевой результат, это следует рассматривать как ошибку, а perror()
или аналогичный следует использовать для сообщения пользователю), это будет выглядеть следующим образом:
setenv("torrent_name", torrent_name_str, 1);
setenv("torrent_category", torrent_category_str, 1);
setenv("save_path", path_str, 1);
# shell script should use "$torrent_name", etc
system(user_provided_shell_script);
Несколько заметок:
- Хотя значения могут быть произвольными строками C, важно, чтобы имена переменных были ограничены - либо жестко закодированные константы, как указано выше, либо префикс с константной (строчной 7-битной ASCII) строкой и проверенный на наличие только символов, которые являются допустимой оболочкой имена переменных. (Рекомендуется использовать префикс в нижнем регистре, поскольку POSIX-совместимые оболочки используют только имена всех заглавных букв для переменных, которые изменяют их собственное поведение; см. спецификацию POSIX для переменных среды , особенно примечание, что " пространство имен имен переменных среды, содержащих строчные буквы, зарезервировано для приложений. Приложения могут определять любые переменные среды с именами из этого пространства имен без изменения поведения стандартных утилит ").
- Пространство среды является ограниченным ресурсом; в современном Linux максимальное объединенное хранилище для переменных среды и аргументов командной строки обычно составляет 128 КБ; таким образом, установка больших переменных среды приведет к сбою семейных вызовов
execve()
с большими командными строками. Мудро подтвердить, что длина находится в разумных пределах для конкретной области.
Использование аргументов командной строки:
Эта версия требует явного API, так что пользователь, конфигурирующий команду триггера, знает, какое значение будет передано в $1
, которое будет передано в $2
и т. Д.
/* You'll need to do the usual fork() before this, and the usual waitpid() after
* if you want to let it complete before proceeding.
* Lots of Q&A entries on the site already showing the context.
*/
execl("/bin/sh", "-c", user_provided_shell_script,
"sh", /* this is $0 in the script */
torrent_name_str, /* this is $1 in the script */
torrent_category_str, /* this is $2 in the script */
path_str, /* this is $3 in the script */
NUL);