Безопасность сценария оболочки
Безопасность часто пропускается при записи сценариев оболочки. Много программистов игнорируют безопасность сценария оболочки под предположением, что что-либо, что атакующий может сделать путем атаки сценария, может быть достигнуто более легко путем простого выполнения самих команд. Однако, когда сценарий берет ввод от недоверяемого третьего лица, это не истина:
Сценарии оболочки, работающие как сценарии CGI на веб-сервере, берут ввод от сети.
Сценарии оболочки, читающие файлы и принимающие меры на основе их содержания, могут взять ввод от недоверяемых файлов.
Сценарии оболочки, выполняющие веб-запросы (с
curl
, например), или другие сетевые запросы могут взять ввод от недоверяемых серверов или клиентов.
Далее, большинство проблем безопасности является также ошибками правильности, даже если кто-то не пытается атаковать Ваш код.
В этой главе описываются несколько частых ошибок в сценариях, показывает, как эти уязвимости могут быть использованы и объясняют, как предотвратить эти атаки в Ваших сценариях.
Эта глава также описывает, как полномочия UNIX и списки управления доступом POSIX (ACLs) влияют на Ваши сценарии и как управлять теми полномочиями и ACLs в Ваших сценариях.
Атаки среды
Атаки переменной окружения являются наиболее распространенным способом управлять поведением сценария. Если сценарий зависит от значений тех переменных окружения, путем управления средой сценария можно изменить его поведение.
Несмотря на то, что они менее вредны для сценариев теперь (потому что сценарии не могут быть выполнены setuid ни в каком современном OS), они могут все еще вызвать неправильное поведение. Для setuid двоичных файлов они еще более опасны. Если один пользователь получает возможность изменить сценарии входа в систему другого пользователя через ошибку или неправильную конфигурацию, эти атаки могут также быть вредными в многопользовательской установке.
Наиболее распространенная атака среды изменяет PATH
переменная окружения. Это управление переменными, что выполняется, когда Вы вводите команду, не давая полный путь.
Рассмотрите следующий код:
#!/bin/sh |
ls /tmp |
Атака:
Создайте исполнимый двоичный файл или сценарий, делающий что-то вредное и называющий его «ls». Тогда сделайте это:
export PATH=/path/to/malicious/binary:$PATH |
/path/to/above/script |
Поскольку путь к злонамеренному двоичному файлу является первым в пути поиска, злонамеренном ls
команда выполняется вместо реальной.
Смягчение:
Всегда указывайте абсолютные или относительные пути при выполнении двоичных файлов или других сценариев. Если Ваш сценарий выполняет другие сценарии или двоичные файлы, не использующие абсолютные или относительные пути внутренне, необходимо явно установить значение PATH
переменная окружения в Ваших сценариях для предотвращения проблем.
Атаки на файлы в публично Перезаписываемых каталогах
Файлы в публично перезаписываемых каталогах, включая временные файлы, уязвимы для атаки путем замены злонамеренным файлом вместо файла, который сценарий намеревался считать или записать.
Временная атака файла
Самым простым примером этой атаки является инструмент, хранящий секретную информацию во временный файл.
Рассмотрите следующий код:
#!/bin/sh |
SECRETDATA="My password is 12345." |
echo > /tmp/mysecretdata |
chmod og-rwx /tmp/mysecretdata |
echo "$SECRETDATA" >> /tmp/mysecretdata |
Атака:
Создайте инструмент, наблюдающий за файлом /tmp/mysecretdata
появиться. (Несмотря на то, что это может быть сделано со сценарием оболочки, это, вероятно, не будет достаточно быстро для работы очень часто. Используйте События Файловой системы API в C вместо этого.)
После обнаружения существования пути сделайте это:
FILE *fp=fopen("/tmp/mysecretdata", "r"); |
Если атакующий управляет открыть файл, прежде чем сценарий выполнится chmod
команда, это может продолжать считывать данные из файла столько, сколько это сохраняет файл открытым.
Смягчение:
Существует две вещи, которые необходимо сделать для фиксации этого:
Всегда используйте
umask
команда для указания первоначальных полномочий на файле, когда Вы создаете его.Всегда создавайте временные файлы с
mktemp
команда. Это создает новый файл с указанным шаблоном, гарантируя, что уже не существуют файл или символьная ссылка с тем именем.
Например:
#!/bin/sh |
SECRETDATA="My password is 12345." |
umask 0177 |
FILENAME="$(mktemp /tmp/mytempfile.XXXXXX)" |
echo "$SECRETDATA" >> "$FILENAME" |
Однако принятие Вас фактически намеревается использовать данные снова в будущем, это смягчение, вероятно, не достаточно также по причинам, описанным в следующей атаке.
Входная атака файла
Подобная атака может быть выполнена на файлах, используемых в качестве вводов к сценариям оболочки.
Рассмотрите сценарий, выполняющий следующий код:
#!/bin/sh |
echo "My password is secret!" > /tmp/mypublicdata |
... |
PUBLICDATA="$(cat /tmp/mypublicdata)" |
echo "$PUBLICDATA" | nc 192.168.1.102 3333 |
Этот сценарий отправляет содержание временного файла для портирования 3333 из другого компьютера в IP-адресе 192.168.1.102 с помощью nc
утилита.
Атака:
Создайте инструмент, наблюдающий за файлом /tmp/mydata
появиться. (Несмотря на то, что это может быть сделано со сценарием оболочки, это, вероятно, не будет достаточно быстро для работы очень часто. Используйте События Файловой системы API в C вместо этого.)
После обнаружения существования пути сделайте это:
unlink("/tmp/mypublicdata"); |
unlink("/etc/myscretdata", "/tmp/mypublicdata"); |
Если атакующий управляет сделать это, прежде чем сценарий считает файл, то Ваш секретный пароль (по-видимому 12345, из предыдущего сценария) отправляется незашифрованный по порту 3333. Атакующий может тогда осуществить сниффинг для трафика на том порту, и может зарегистрировать в Вашу учетную запись (или по крайней мере разблокировать Ваш багаж).
Смягчение:
Это особенно неприятно для смягчения, потому что инструменты UNIX по сути идут по символьным ссылкам. Единственный способ решить проблему состоит в том, чтобы избежать писать фактические файлы в общедоступные каталоги. Необходимо сделать это следующим образом:
Всегда создавайте временные каталоги с
mktemp
команда, затем создайте свои фактические временные файлы в тех каталогах. Путем выполнения этого можно установить строгие полномочия на каталоге, который будет препятствовать тому, чтобы атакующий удалил файлы и заменил их.Если Вы указываете
-d
флаг,mktemp
команда создает новый каталог с указанным шаблоном, гарантируя, что уже не существует файл или каталог с тем именем.Всегда используйте
umask
команда для указания первоначальных полномочий на файлах и каталогов, когда Вы создаете их.
Например:
#!/bin/sh |
umask 0177 |
TMPDIR="$(mktemp -d /tmp/mytempfile.XXXXXX)" |
echo "My password is secret!" > "$TMPDIR"/mypublicdata |
... |
PUBLICDATA="$(cat "$TMPDIR"/mypublicdata)" |
echo "$PUBLICDATA" | nc 192.168.1.102 3333 |
Инжекционные атаки
Наиболее распространенный тип атаки в сценариях оболочки является инжекционной атакой. Когда параметры, сохраненные в предоставленных пользователями переменных, передаются командам без надлежащего заключения в кавычки, этот тип атаки происходит.
Простой пример
Рассмотрите следующий пример:
read FOO |
read BAR |
if [ x$FOO = xfoo ] ; then |
echo $FOO |
eval $BAR |
fi |
Этот код имеет две дыры в системе безопасности. Можно ли определить их?
if [ x$FOO = xfoo ] ; then
Этот оператор допускает инжекционную атаку на
FOO
.Атака:
Передача “
foo = xfoo -o x
” как значение дляFOO
.Несмотря на то, что значение
FOO
не «foo», оператор выполняется так или иначе. В зависимости от какого этот тест делает, это могло потенциально вызвать неожиданное поведение.Смягчение:
Для исправления этой ошибки изменитесь если оператор для чтения:
if [ "$FOO" = "foo" ] ; then
eval $BAR
Это нет - нет. Никогда не работайте, оценка на данных передала в пользователем, если Вы очень, очень тщательно не санировали его (и, если возможно, используйте whitelist для ограничения позволенных значений).
Атака:
Передайте опасную команду для
BAR
.Смягчение:
Просто не делайте этого.
Тонкий пример
Следующий пример является более тонким. Вместо выполнения eval
, это пишет данные в сценарий, но делает так, не защищая значения:
#!/bin/sh |
read FOO |
# ... |
echo ls $FOO >> myscript.sh |
# ... |
chmod a+x myscript.sh |
./myscript.sh |
Атака:
Передайте значение “; rm randomfile
” чтобы заставить этот сценарий удалять файл.
Неправильное смягчение:
Вы могли бы испытать желание исправить эту ошибку путем изменения эха и строк выполнения для чтения:
echo ls "\"$FOO\"" >> myscript.sh |
export FOO |
Однако это все еще не решает проблему потому что FOO
сразу расширен, что означает что если значение FOO
содержит кавычку — например, “";rm randomfile ; echo "
”, у Вас теперь есть различное (но одинаково плохо) дыра в системе безопасности.
Корректное смягчение № 1:
Один способ исправить эту ошибку состоит в том, чтобы изменить строку эха для чтения:
echo ls "\"\$FOO\"" >> myscript.sh |
Это вызывает переменную FOO
быть расширенным, когда выполняется сценарий. Однако это работает только если переменная FOO
экспортируется, потому что иначе переменная FOO
ни до чего не расширился бы во втором сценарии.
Корректное смягчение № 2:
Другой способ исправить эту ошибку состоит в том, чтобы изменить строку эха для чтения:
QUOTFOO="$(echo "$FOO" | sed "s/'/'\"'\"'/g")" |
echo ls "'$QUOTFOO'" >> myscript.sh |
При помощи одинарных кавычек вокруг строки во вторичном сценарии единственный символ, относящийся к оболочке, является символом одинарной кавычки. sed
команда тогда заменяет любые символы одинарной кавычки в строке с заключительной одинарной кавычкой, сопровождаемой одинарной кавычкой, обернутой в двойные кавычки, сопровождаемые вводной одинарной кавычкой.
Назад пример совместимости
Следующий пример не опасен в современных оболочках, но опасен в более старых Оболочках Bourne:
#!/bin/sh |
read FOO |
echo $FOO |
Атака:
Передайте значение “; rm randomfile
” чтобы заставить этот сценарий удалять файл в более старых оболочках.
Самые современные оболочки анализируют оператор до любой подстановки переменных и таким образом незатронуты этой атакой. Однако для надлежащей безопасности, когда Ваш сценарий выполняется в более старых системах (чтобы не упомянуть, что избежали синтаксической ошибки, если имя файла содержит пробелы), необходимо все еще окружить переменную двойными кавычками.
Смягчение:
Для исправления этой ошибки измените строку эха для чтения:
echo "$FOO" |
Атаки аутентификации
В целом Вы не должны полагаться на сценарий, чтобы определить, делает ли пользователь или не имеет разрешения сделать что-то. Это неуклюже и подвержено ошибкам. Это возможно для этого, однако, и существуют правильные и неправильные способы сделать это.
Неправильный путь:
if [ $UID = 100 -a $USER = "myusername" ] ; then |
cd $HOME |
fi |
Этот код имеет три ошибки безопасности, и они все вызываются при помощи переменных способами, которые небезопасны. Для исторической совместимости OS обеспечивает UID
, USER
, и HOME
переменные окружения. Они довольно полезны, пока Вы не используете их для соображений безопасности.
Атака:
$ tcsh |
% setenv UID 100 |
% setenv USER myusername |
% setenv HOME $HOME/.ssh |
% /path/to/script.sh |
Даже при том, что большинство современных Оболочек Bourne защищает от изменения UID
, USER
переменная незащищена, и не все оболочки защищают UID
переменная, также.
К счастью, сценарий просто изменился в каталог. Объединенный с другой годной для использования атакой, такой как инжекционная атака, однако, это могло быть использовано плохими способами.
Смягчение:
Получить идентификатор пользователя:
# Effective UID |
MYEUID="$(/usr/bin/id -u)" |
# Real UID |
MYUID="$(/usr/bin/id -u -r)" |
Получить имя пользователя:
MYUID="$(/usr/bin/id -u -n)" |
Получить фактический корневой каталог:
HOMEDIR="$(dscl . -read /Users/dg NFSHomeDirectory | sed 's/^NFSHomeDirectory: //')" |
Обратите внимание на то, что этот метод для получения корневого каталога является определенным для OS X.
Полномочия и списки управления доступом
OS X использует модель полномочий UNIX, расширенную списками управления доступом POSIX. Эти модели полномочий описаны подробно в разделе OS X File System Security Руководства по программированию Файловой системы. Этот раздел предполагает, что Вы уже, по крайней мере, периферийно знакомы с понятием о пользователях и группах.
Исследование полномочий файла
Полномочия UNIX видимы пользователям в Терминале и в окне Finder's Get Info. В Терминале можно легко смотреть на полномочия в человекочитаемом формате при помощи ls
команда следующим образом:
$ ls -ld filename dirname |
drwxr-xr-x 2 username groupname 68 Jun 16 13:40 dirname |
-rw-r--r-- 1 username groupname 0 Jun 16 13:40 filename |
Левый символ указывает, является ли объект файловой системы файлом (-
), каталог (d
), символьная ссылка (l
), блок (b
) или символ (c
) специальный файл, именованный канал (p
), или сокет домена UNIX (s
).
Следующие три символа показывают полномочия Владельца, сопровождаемые полномочиями Группы, и наконец, Другие полномочия, как перечислено в следующей таблице:
Флаг Permissions | Восьмеричное битовое значение | Значение |
---|---|---|
- | n/a | Никакое разрешение |
r | 4 | Считайте разрешение |
w | 2 | Запишите разрешение |
x | 1 | Выполните разрешение |
s | В дополнительной первой восьмеричной цифре:
| Setuid или setgid с выполняют разрешение |
S | Посмотрите выше. | Setuid или setgid |
t | В дополнительной первой восьмеричной цифре: 1 | Липкий бит |
Полный набор полномочий часто выражается в восьмеричном, как определено битами в таблице выше. Первая цифра включает липкий бит и setuid и setgid биты. Если нуль, можно опустить его при передаче значения большинству команд. Оставление тремя цифрами содержит Владельца (пользователь), Группа и Другие полномочия, соответственно.
Например, файл, который является setuid и setgid, с, читал/писал/выполнял полномочия Владельца и читал/выполнял Группу и Другие полномочия, восьмеричный эквивалент 6755:
Ведущее специальное значение полномочий равняется 6, который является битовым «ИЛИ» setuid (4) и setgid (2).
Разрешение Владельца равняется 7, который является битовым «ИЛИ» чтения (4), запишите (2) и выполнитесь (1) биты.
Группа и Другие полномочия и 5, который является битовым «ИЛИ» чтения (4), и выполнитесь (1) полномочия.
Для показа полномочий UNIX файла используйте команду статистики следующим образом:
stat -f "%p" filename |
Проигнорируйте все кроме последних четырех цифр возвратились.
Изменение принадлежности файла и полномочий
Возможность изменить принадлежность файла и полномочия ограничивается операционной системой по причинам квоты и безопасности. Пользователи могут:
Измените полномочия для любого файла, которым они владеют.
Измените группу для любого файла, которым они владеют любой группе, из которой они являются участником.
Некорневые пользователи не могут:
Полномочия изменения на файлах принадлежат кому-либо еще.
Измените группу файла группе, из которой они не являются участником.
Измените владельца любого файла.
Пользователь root может изменить полномочия и владение произвольно кроме тех случаев, когда блокированный файловой системой BSD отмечает.
С теми ограничениями в памяти, следующие разделы описывают, как изменить полномочия и изменить пользователя и владение группы файлов и каталогов.
Используйте chown и chgrp для Изменения Владения Групп и Пользователя
Можно изменить владельца файла или каталога с chown
команда:
# Change the owner of a file or directory |
sudo chown newowner filename_or_dirname |
# Change the owner of a directory and everything in it recursively |
sudo chown -R newowner dirname |
Можно изменить группу для файла с любым chown
команда или chgrp
команда:
# Change the group by itself |
chown :newgroup filename_or_dirname |
chgrp newgroup filename_or_dirname |
# Change the group of a directory and everything in it recursively |
chown -R :newgroup dirname |
chgrp -R newgroup dirname |
Можно также изменить и владельца и группу одновременно:
# Change the owner and the group |
sudo chown newowner:newgroup filename_or_dirname |
# Change the group of a directory and everything in it recursively |
sudo chown -R newowner:newgroup dirname |
Для получения дополнительной информации см. страницы руководства для chown
и chgrp
.
Используйте chmod для Изменения Полномочий Файла и Каталога
OS X (и другие основанные на UNIX операционные системы) обеспечивает chmod
команда для изменения полномочий файлов и каталогов.
chmod
команду, короткую для “режима изменения”, так называют, потому что это позволяет Вам изменять режимы файла или каталога. Режим является трехразрядным или четырехразрядным восьмеричным представлением полномочий UNIX для файла (или 4-5 цифр на языках, требующих начального нуля, такого как C).
Существует два основных способа, которыми можно использовать chmod
команда: числовые режимы и человекочитаемые флаги.
Большая часть пользовательского использования chmod
в его человекочитаемой форме:
chmod a+rw world_writable_file |
Эта команда говорит chmod
добавить чтение (r
) и запишите (w
) доступ к существующему набору полномочий для всех пользователей (a
). Таким образом, если полномочия были первоначально r-x--x-w-
, получающиеся полномочия были бы rwxrwxrw-
.
Можно также добавить и вычесть полномочия для пользователя владения (u
), группа (g
), или другие пользователи (o
) отдельно. Например, для добавления чтения (r
), запишите (w
), и выполнитесь (x
) разрешение для пользователя владения и устраняет его от участников группы владения и всех остальных, Вы могли дать любую из следующих команд:
chmod u+rwx,g-rwx,o-rwx filename |
chmod u+rwx,go-rwx filename |
chmod a-rwx,u+rwx filename |
Точно так же можно установить Пользователя, Группу или Другие полномочия вне зависимости от того, какие биты были установлены, прежде при помощи равняется. Например, для установки полномочий группы читать, без записей, нет - выполняются, Вы могли дать следующую команду:
chmod g=r filename |
Наконец, чтобы заставить исполнимую программу выполнить setuid (u+s
) и setgid (g+s
), Вы могли бы выполнить команду как одно из следующего:
chmod a+rx,ug+s filename |
chmod a+rxs filename # Note: o+s is ignored. |
Также, если Вы знаете числовой режим файла, Вы хотите применяться (см. Полномочия Файла Исследования для подробных данных), можно передать chmod
команда или трехразрядное или четырехразрядное значение режима:
chmod 666 world_writable_file |
chmod 0666 world_writable_file |
chmod команда может также использоваться для изменения списков управления доступом POSIX (ACLs). Это использование описано позже в Использовании chmod для Изменения Списков управления доступом.
Используйте chflags для Установки Специальных Флагов Разрешения Файла
В дополнение к стандартным флагам разрешения OS X имеет несколько специальных флагов разрешения, которые могут быть установлены с помощью chflags
или lchflags
команда (или с chflags
или fchflags
API в C). Эти флаги описаны в разделе OS X File System Security Руководства по программированию Файловой системы.
Набор флагов полномочий с chflags
имейте приоритет по любым разрешениям, данным нормальными полномочиями UNIX или списками управления доступом.
Использование chflags
команда является довольно прямой. Например, для создания файла неизменным (так, чтобы это не могло быть перемещено, переименованное, удаленное или изменило), можно дать одну из следующих команд:
chflags uchg filename # user flag |
sudo chflags schg filename # system flag |
Заметьте, что флаг прибывает в два варианта: пользовательский флаг и системный флаг. Пользовательский флаг может быть изменен владельцем файла и root
(точно так же, как нормальные полномочия). Системный флаг может быть изменен исключительно root
.
Для отмены этого изменения Вы дали бы одну из следующих команд:
chflags nouchg filename # user flag |
sudo chflags noschg filename # system flag |
По межплатформенным причинам совместимости и удобочитаемости OS X поддерживает два других изменения на каждом из этих флагов: uchange
, uimmutable
, schange
, и simmutable
. Эти варианты ведут себя тождественно к их сокращенным формам.
Существует несколько других флагов, которые можно установить с chflags
команда, наиболее распространенное существо пользователь и системные флаги только добавления (uappnd
/uappend
и sappnd
/sappend
, соответственно).
Для получения дополнительной информации читайте chflags
и lchflags
страницы руководства и раздел OS X File System Security Обзора безопасности.
Используйте chmod для Изменения Списков управления доступом
chmod
команда обычно известна ее возможностью изменить полномочия UNIX. Однако в OS X, это также удваивает режим работы, обеспечивая интерфейсы сценариев для изменения списков управления доступом POSIX файла (ACLs).
Фундаментальное понятие ACLs является довольно прямым. Список управления доступом является списком правил (элементы списка управления доступом или ACEs).
Каждая запись предоставляет или отклоняет право получить доступ к файлу или каталогу определенным способом (право считать файл, например).
Для любого предоставленного права, первой записи в списке, соответствующем против идентификатора пользователя текущего пользователя или побед состава группы.
Если конец списка достигнут, ничего не соответствуя, полномочия UNIX файла или каталога используются для определения доступа.
Это - значительно упрощенное объяснение; для полного изложения считайте раздел OS X File System Security Обзора безопасности.
Каждая запись ACL похожа на это:
username grant rightname |
groupname grant rightname |
username deny rightname |
groupname deny rightname |
где имя пользователя и groupname являются именами пользователя или группы, соответственно, и rightname является именем права доступа (read
, например).
Можно добавить элемент списка управления доступом с +a
флаг к chmod
. Например, для отклонения доступа для чтения на файле пользователю MySQL Вы ввели бы:
chmod +a "_mysql deny read" filename |
Для наблюдения результатов изменений введите:
ls -le filename |
По умолчанию новые записи списка управления доступом добавляются до конца списка. Если необходимо вставить управление доступом в другом месте в список, можно использовать +a#
флаг. Например, для вставки нового правила в нуле позиции (верхняя часть списка) Вы дали бы команду как этот:
chmod +a# 0 "_www deny read" filename |
Можно удалить элемент списка управления доступом с -a
отметьте как это:
chmod -a "_mysql deny read" filename |
Эта команда удаляет любую запись, которая является точным совпадением для указанного правила.
Наконец, можно заменить запись другой записью с помощью =a#
флаг. Например, для изменения имени пользователя в правиле, вставленном выше от _www
к _mdnsresponder
, Вы ввели бы:
chmod =a# 0 "_mdnsresponder deny read" filename |
В дополнение к основным правилам, описанным выше, система ACL в OS X поддерживает наследование. Любой наследовался, записи ACL для каталога автоматически копируются в любые новые файлы, создаваемые в том каталоге во время создания.
Можно указать:
должен ли ACL быть наследован:
вложенные файлы —
file_inherit
правокаталоги —
directory_inherit
правооба —
file_inherit,directory_inherit
правони один (значение по умолчанию).
должен ли ACL быть наследован дочерними элементами вложенных каталогов (значение по умолчанию) или не (
limit_inherit
право).должен ли ACL примениться к самому каталогу (значение по умолчанию) или просто быть наследован вещами в нем (
only_inherit
право).
Можно указать любую комбинацию этих флагов в элементе списка управления доступом для каталога путем передачи флагов как части списка прав.
Например:
chmod +a "_www deny list,search,directory_inherit" dirname |
Это правило предотвращает _www
пользователь от перечисления содержания каталога. Это также предотвращает _www
пользователь от доступа к любым файлам в указанном каталоге даже с точным поиском имени (поиск). Правило наследовано любым новым каталогом, создаваемым в указанном каталоге (и любом каталоге, создаваемом в том, и т.д.), но не наследовано обычными файлами.
Для получения дополнительной информации о схеме ACL в OS X описан в разделе OS X File System Security Обзора безопасности. Для получения дополнительной информации о флагах командной строки для получения и установки ACLs, см. страницу руководства для chmod
.
Обеспечение временных файлов
Поскольку временные каталоги в OS X и других основанных на UNIX операционных системах являются мировыми перезаписываемыми, необходимо заботиться, чтобы гарантировать изменение файла, Вы думаете, что изменяете.
Например, следующий код имеет две серьезных ошибки:
if [ ! -f /tmp/mytempfile ] ; then |
# Race condition here |
touch /tmp/mytempfile |
chmod u=rw,og= /tmp/mytempfile |
# Missing error check here |
echo My secret password is omnibus > /tmp/mytempfile |
fi |
Приложение, которое, оказывается, разбирается в синхронизации, может создать вызванный файл /tmp/mytempfile
прямо после проверок сценария на его существование, ожидайте сценария, чтобы записать данные в него, и впоследствии украсть пароль. chmod
команда произвела бы ошибку в этом случае, но потому что сценарий не проверяет код результата, ошибка спорна.
Для решения этой проблемы всегда используйте mktemp
команда для создания временных файлов. mktemp
команда создает файлы с первоначальными полномочиями 0600, и никогда не возвращает существующий файл. (Используя mktemp
также обеспечивает простой способ получить известное - уникальное имя файла, потенциально избегая неожиданного поведения, вызванного временными коллизиями файла.)
Флаги, влияющие на безопасность (и правильность)
set
встроенный (описанный в sh
страница справочника), устанавливает много функций оболочки, которые могут быть использованы для снижения риска, изложенного определенными типами общих ошибок программирования. Эти флаги позволяют Вашим сценариям автоматически выходить, если переменная сброса расширена, автоматически выйдите, если какие-либо простые команды перестали работать, или автоматически экспортируют переменные.
Кроме того, оболочка BASH обеспечивает флаг, заставляющий каналы возвращать ненулевой статус выхода, когда любая команда в цепочке каналов выходит с ошибкой вместо того, чтобы всегда возвратить статус выхода последней команды. Это также поддерживает флаг, ограничивающий эффект переменных окружения на интерпретаторе, предназначенном для использования в сценариях, которые, как ожидают, будут выполнены как привилегированный пользователь (например, root
пользователь).
Обнаружение переменных сброса
По умолчанию Оболочка Bourne обрабатывает переменные сброса как пустые (в отличие от этого csh
). Если Ваш сценарий ожидает, что переменная сброса для содержания значения это может привести к неправильному выполнению сценария и, в зависимости от сценария, может даже привести к дыре в системе безопасности. Для принятия мер против этого можно дать следующую команду:
set -u |
С этим набором флага, если Ваш сценарий пытается использовать пустую переменную, оболочка распечатывает сообщение об ошибке, и весь сценарий сразу выходит с ненулевым статусом выхода.
При желании можно позже восстановить поведение по умолчанию со следующей командой:
set +u |
Проверка статуса выхода автоматически
Для очень простых сценариев, проверяя статус выхода каждой команды может быть утомительным. Можно значительно упростить эти сценарии, вместо этого дав следующую команду:
set -e |
С этим набором флага, если какие-либо простые выходы команды с ненулевым статусом выхода, оболочка завершается со статусом выхода той команды. Простая команда определяется как команда, не включающая каналов или списков, который не выполняется как часть оператора управления, и чей статус выхода не инвертируется с восклицательным знаком.
При желании можно позже восстановить поведение по умолчанию со следующей командой:
set +e |
Экспорт переменных автоматически
Не всегда необходимо экспортировать переменные, которые Ваш сценарий использует внутренне. Однако, если дочерний процесс зависит от значений тех переменных, они должны быть экспортированы. В некоторых случаях сбой экспортировать переменную мог даже привести к дыре в системе безопасности, если бы он заставляет дочерний элемент предоставлять пользовательский доступ, который он или она иначе не имел бы. Например, если сценарий CGI, работающий в среде веб-сервера, обеспечивает дополнительные пределы на том, к каким файлам удаленный пользователь может получить доступ, ошибка в том сценарии могла бы предоставить пользовательский доступ к другим файлам.
Можно при желании сказать оболочке автоматически экспортировать любую переменную, которую сценарий устанавливает путем выдачи следующей команды:
set -a |
При желании можно позже восстановить поведение по умолчанию со следующей командой:
set +a |
Получение статуса выхода переданных по каналу команд в BASH
Статус выхода ряда команд, соединенных каналами, является, по умолчанию, статусом выхода самой правой команды. Если Вы не исследуете вывод от заключительной команды, чтобы гарантировать, что это целесообразно, это поведение по умолчанию может потенциально замаскировать ошибки, которые могли бы привести к проблемам безопасности.
Например, рассмотрите следующий код:
ls nonexistentfile | cat |
echo $? |
В первой команде, даже при том, что ls
сбои команды, команда кошки не заботится, получила ли она какой-либо ввод или нет, и таким образом выходит с нулевым статусом выхода. В результате статус выхода канала является нулем. Если критически важно знать, перестала ли первая команда работать (например, если это выполняет работу с важным побочным эффектом, таким как удаление файла на диске), то это потенциально небезопасно.
Существует много способов, которыми можно решить эту проблему. Самая очевидная фиксация должна сохранить результаты первой команды в переменную временно, проверить код результата первой команды, и затем использовать echo
передавать результаты по каналу к второй команде. Этот метод часто является меньше, чем идеал для команд, занимающих много времени, чтобы выполнить или произвести большие суммы вывода, однако, потому что вторая команда не получает данных до окончания первых выходов команды. Если вывод заключительной команды, как ожидают, будет считан пользователем, влияние производительности особенно примечательно.
Как альтернатива, в BASH, можно дать следующую команду прежде, чем дать команды выше:
set -o pipefail |
После выдачи этой команды статус выхода канала предоставлен самой правой командой, переставшей работать с ненулевым статусом выхода или нулем если каждая команда в цепочке каналов, из которых выходят успешно. В более раннем примере, финале echo
команда распечатала бы число 1
(статус выхода ls
команда).
При желании можно позже восстановить поведение по умолчанию со следующей командой:
set +o pipefail |
Очистка среды в BASH
Для сценариев оболочки BASH (или сценарии Оболочки Bourne, работающие в BASH), который должен работать в привилегированной среде (как root
пользователь, например), это - хорошая идея сказать, что оболочка к не автоматически выполняет любые файлы «команд выполнения» (.bashrc
, .profile
, и т.д.), который может содержать команды псевдонима, влияющие на выполнение сценария, функции, которые могут переопределить команды в Вашем сценарии или даже злонамеренные команды, которые атакующий хочет, чтобы Ваш сценарий выполнил при выполнении как root
пользователь.
Для очистки среды сценария таким образом необходимо изменить строку интерпретатора сценария на следующее:
#!/bin/bash -p |
В этом режиме, сценарии, на которые ссылаются ENV
и BASH_ENV
переменные окружения не выполняются, окружают функции, не наследованы, и SHELLOPTS
переменная окружения проигнорирована.