Проверка безопасного ввода в сценарии Bash / Shell

Я пишу сценарий bash, который требует ввода нескольких частей пользователя, в первую очередь путей к каталогам и имен файлов. Программа создаст указанные каталоги и файлы.

При написании пути к каталогу я запрашиваю абсолютный путь - ~ / DIR / ..., или $ PATH / DIR / ... или ./DIR / ... не допускаются. Пути к каталогам следует указывать только в форме / DIR / DIR / ..., пути к файлам - в форме /DIR/DIR/.../filename и имена файлов в виде имени файла. Я также хочу убедиться, что пользователь не добавил никаких параметров командной строки (например, если я запускаю sudo, я хочу убедиться, что пользователь не добавил -u где угодно в их заявлении) к их входным данным.

Однако я не совсем уверен в том, как лучше всего очистить их ввод, поскольку пути к каталогам и имена файлов могут содержать дефисы, подчеркивания, пробелы, точки и буквенно-цифровые символы. Любой другой персонаж должен быть обнаружен.

Пользовательский ввод собирается посредством чтения. Я использую систему Ubuntu, поэтому, если она соответствует оболочке Ubuntu, она будет работать. Предположим, что установлены только системные пакеты по умолчанию.

Для обработки отдельных разделов проверки допустимо несколько параметров (например, использование -- для обработки следующих данных как параметра, а не команды [предложено Siguza], а затем еще один вариант для обработки путей к каталогам).

Абсолютные пути необходимы для правильного отображения структуры каталогов. Любой другой формат пути приведет к тому, что программа неправильно интерпретирует структуру при сопоставлении или добавлении к другим путям из-за функций, которые я выполняю над строками путей.

Спасибо.


Что сработало: я использовал принятый ответ, в некоторых случаях используя realpath и readlink, а также используя - для интерпретации следующего текста как параметров. Если вы хотите разрешить относительные пути, используйте в качестве пути результат readlink или realpath. Если вы хотите запретить относительные пути, сравните исходную строку с результатом readlink или realpath.


person Logan Hartman    schedule 20.08.2016    source источник
comment
Большинство команд поддерживают параметр --, что позволяет им рассматривать все следующие параметры как аргументы, а не параметры / флаги. Кроме того, заключите все переменные в двойные кавычки, чтобы избежать раскрытия.   -  person Siguza    schedule 20.08.2016
comment
Хотя это решает первые две проблемы, по-прежнему остается, что пользователь может целые пути к каталогам в формате, который моя программа не может принять. Мне нужно либо отклонить недопустимый ввод, либо согласовать его с действительным вводом.   -  person Logan Hartman    schedule 20.08.2016
comment
Re: пользователь может целые пути к каталогам в формате, который моя программа не может принять. Мне нужно либо отклонить недопустимый ввод, либо согласовать его с допустимым вводом: Или вы можете изменить свою программу, чтобы она принимала такие форматы. . .   -  person ruakh    schedule 21.08.2016
comment
@LoganHartman: Я не уверен, что вы имеете в виду, говоря, что сделать сценарий более сложным без всякой выгоды. Весь ваш вопрос заключается в том, чтобы помочь сделать сценарий более сложным с целью отклонения путей, которые вы не поддерживаете; Чем это лучше, чем просто поддерживать их?   -  person ruakh    schedule 21.08.2016
comment
@LoganHartman: Но в любом случае, если у вас есть законные причины для ограничения вашего ввода, вы должны объяснить их в вопросе. Возможно, ваши причины будут соответствовать известным решениям. (Например, мне нужен путь, который можно безопасно передать в утилиту foo, можно ответить с помощью: о, есть специальная check_path_for_foo утилита, которая может сделать эту проверку безопасности за вас.)   -  person ruakh    schedule 21.08.2016
comment
@LoganHartman: mkdir, cd и cp не имеют никаких ограничений на имена файлов, которые они принимают; что заставило вас думать, что они сделали? Re: Я сравниваю их со статическими полными путями: какое сравнение вам нужно? Обратите внимание, что два пути могут быть как абсолютными, так и совершенно разными, но указывать на один и тот же файл. Поэтому, пожалуйста, отбросьте свои ошибочные сомнения и отредактируйте свой вопрос, чтобы добавить соответствующие сведения о том, что вам действительно нужно.   -  person ruakh    schedule 21.08.2016
comment
Литералы с ~/DIR или $DIR или еще чем-то, если вы правильно цитируете, не будут расширены до того, как они будут переданы в ваш mkdir или cp или что-то еще, поэтому, если у вас нет имени файла с ~ или $ в нем, он просто придет - не найден. Есть ли причина, по которой вы не можете просто обработать эту ошибку, когда она возникает?   -  person Charles Duffy    schedule 21.08.2016
comment
Для ясности: допустимые пути в UNIX могут обычно [POSIX не требует этого, но почти каждая основная файловая система UNIX позволяет] содержать буквально любой символ, отличный от NUL, включая символы новой строки. Таким образом, ваш while read довольно глючен: имена файлов с литералами с обратной косой чертой лишены их (если не удвоены); завершающие пробелы удаляются самой командой read, если IFS не очищен; и если запись действительно заканчивается обратной косой чертой, read считает, что строка после является частью той же записи!   -  person Charles Duffy    schedule 21.08.2016
comment
... теперь, если вы обрабатываете поток ввода с разделителями NUL, вы можете представить все возможные входы с помощью IFS= read -r -d '' path или, если вы хотите поддерживать любой символ, кроме новой строки, IFS= read -r path.   -  person Charles Duffy    schedule 21.08.2016
comment
@CharlesDuffy Этот сценарий может запускаться как в файловых системах UNIX, так и в общих файловых системах (SAMBA и т. Д.), И поэтому он должен предполагать какое-то соответствие, поэтому я игнорирую некоторые символы. Прочитав ваши комментарии, я изменил код, который использую для проверки / расширения путей для учета таких ситуаций.   -  person Logan Hartman    schedule 21.08.2016
comment
Попался! Учитывая этот вариант использования, могу ли я предложить вам использовать набор символов переносимого имени файла POSIX в качестве минимальной базовой линии? Это обеспечивает минимальный базовый уровень, ниже которого любая файловая система не соответствует требованиям.   -  person Charles Duffy    schedule 21.08.2016
comment
@CharlesDuffy, если этот набор символов - это то, о чем вы говорите, по сути, это то, что я использую, только с добавлением символа пробела.   -  person Logan Hartman    schedule 21.08.2016


Ответы (1)


Вы можете сделать что-то вроде этого:

#!/bin/bash

set -e    

while read path; do
    result=`realpath -m -- "$path"`
    if [ "$result" != "$path" ] && [ "$result/" != "$path" ] ; then
        echo "Rejected: $path"
    else
        last_char_index=$((${#path}-1))
        last_char=${path:$last_char_index:1}
        if [ "$last_char" == "/" ]; then
            echo "New directory: $path"
            mkdir -- "$path"
        else
            echo "New file: $path"
            touch -- "$path"
        fi
    fi
done

exit 0

Я использую realpath для перехвата относительных путей, затем всегда использую ввод данных пользователем в двойных кавычках, чтобы избежать внедрения команд (если это возможно). Думаю, есть способы получше, но на данный момент это все, о чем я могу думать.

Изменить: как рекомендовано в комментариях, я добавил --, чтобы имена файлов / каталогов были безопасно присвоены touch, mkdir и realpath.

person yoones    schedule 20.08.2016
comment
Заключение переменных в кавычки не препятствует их синтаксическому анализу как параметрам. Используйте для этого --. - person Siguza; 20.08.2016
comment
@Siguza Не могли бы вы привести пример, в котором этот сценарий небезопасен? - person yoones; 20.08.2016
comment
Я никогда не говорил, что это можно использовать, но вы можете установить path = -X и получить сбой realpath при недопустимой опции. Вы также должны set -e;, иначе вы можете установить path = '', и, поскольку сообщения об ошибках выводятся на stderr, а не на stdout, realpath выйдет из строя, но блок else будет выполнен. Опять же, использовать нельзя, но, вероятно, нежелательно. - person Siguza; 21.08.2016
comment
Интересный. Мне придется попробовать это, когда я вернусь домой, и посмотрю, сработает ли это. - person Logan Hartman; 21.08.2016
comment
Я принял это как ответ, потому что с небольшими вариациями он действительно работал. Я не использовал заданную часть, потому что я индивидуально проверяю каждую команду на статус выхода, чтобы показать пользователю, какой код статуса был в случае сбоя программы. Хотя «realpath» работает в некоторых ситуациях, я обнаружил, что «readlink -f» работает лучше в других. @yoones Спасибо. - person Logan Hartman; 21.08.2016
comment
В общем случае следует использовать аргумент read -r, если у вас нет веской причины его пропустить. Как бы то ни было, обратная косая черта во вводе будет рассматриваться как синтаксическая. - person Charles Duffy; 21.08.2016
comment
@CharlesDuffy Спасибо. Я этого не уловил; Я просто соответствующим образом изменил это в своем коде. - person Logan Hartman; 21.08.2016
comment
Ох - и я замечаю, что == передается [ ]; это должно быть зарезервировано для [[ ]], поскольку это расширение bash, отсутствующее в стандарте POSIX для команды test; см. pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html, указав = в качестве оператора сравнения строк. - person Charles Duffy; 21.08.2016
comment
@CharlesDuffy Упс. Тоже не уловил. На самом деле не стоит писать скрипты поздно ночью. Еще раз спасибо. - person Logan Hartman; 21.08.2016