Будьте осторожны при чтении данных



Будьте осторожны при чтении данных

При чтении важных данных, скажем, конфигурационных файлов, сначала протестируйте возможность небезопасных состояний. Например, стоит проверить, запрещена ли запись в файл и каталоги, в которых он находится (иначе, кто угодно может их испортить). Хороший способ подобной проверки можно найти в главе 8 книги «Perl Cookbook» («Perl: библиотека программиста») Тома Кристиансена (Тот Christiansen) и Натана Торкингтона (Nathan Torkington) (O'Reilly).

Другая забота - ввод пользователей. Никогда не считайте, что данным, поступающим от пользователей, можно доверять. Даже если вы явно просите пользователя: Пожалуйста, ответьте Да(У) или Нет(М):, ничто не помешает ему набрать 2049 случайных символов (либо от вредности со злым умыслом, либо потому, что его двухлетний ребенок занял освободившееся на минуту место за клавиатурой).

Ввод пользователей может быть причиной еще более серьезных проблем. Мой любимый пример- это использование нулевого байта «Poison NULL Byte», о котором сообщалось в статье о проблемах Perl в CGI. Обязательно прочитайте всю статью (ссылка на нее есть в конце этой главы). Неприятности возникают из-за отличий в обработке нулевого байта (\000) в Perl и в системных библиотеках С. Для Perl этот символ ничем не примечателен. Однако в библиотеках этот символ используется для обозначения конца строки.

На практике это означает, что у пользователя существует возможность обойти различные проверки. Один пример, приведенный в этой статье, - это программа, меняющая пароль пользователя:

if (Suser ne "root"){ <вызов соответствующей функции С>}

Если переменная $user установлена в значение root\000 (т. е. если за словом root следует нулевой байт), то приведенная выше проверка окажется удачной. Когда эта строка будет передана библиотеке, она будет воспринята просто как root, и пользователю удастся обойти проверку. Если эту ситуацию не отследить, то подобная «дыра» позволит получить доступ к произвольным файлам и другим ресурсам. Самый простой способ не пострадать от этой проблемы - подправить код, добавив что-то похожее на следующую строку:

Sinput =" tr/\000//d;

Это всего лишь один пример того, как ввод пользователя может вызвать проблемы. Именно поэтому в Perl существует специальное средство предосторожности - режим пометки (taint mode). Изучите страницу руководства perlsec, входящую в состав дистрибутива Perl, чтобы ознакомиться с отличным объяснением того, что такое отмеченные данные, а также с другими мерами предосторожности.

Будьте осторожны при записи данных

Если ваша программа может записывать или дописывать данные в любой файл локальной файловой системы, вы должны особенно заботиться о том, как, куда и когда записываются данные. В системах Unix это особенно важно, поскольку символические ссылки очень сильно упрощают подмену файлов и перенаправление. Если ваша программа написана не очень аккуратно, может оказаться, что она пишет не в тот файл или устройство. Существует два класса программ, в которых это соображение особенно важно.

В первый класс попадают программы, дописывающие данные в существующие файлы. Перед дописыванием в файл в вашей программе должна быть выполнена следующая последовательность шагов:

  1. Используйте функцию stat() и обычные операторы проверки файлов для проверки атрибутов файлов. Убедитесь, что файл не является ни жесткой, ни символической ссылкой, что у него установлены нужные права и владельцы и т. д.
  2. Откройте файл для дописывания.
  3. Передайте файловый дескриптор функции stat().
  4. Сравните значения, полученные на шагах 1 и 3, чтобы убедиться, что открытый файловый дескриптор соответствует нужному вам файлу.


Смотрите программу bigbuffy из главы 9 «Журналы», которая соблюдает эту последовательность шагов.

Во второй класс попадают программы, использующие временные файлы или каталоги. Вы часто видели подобный код:

open(TEMPFILE,">/tmp/temp.$$") or die "невозможно записать в /tmp/ temp.$$:$!\n";

К сожалению, это недостаточно безопасно для многопользовательских систем. Последовательность идентификаторов процессов ($$) на большинстве машин легко предсказуема, а это означает, что также предсказуемо имя следующего временного файла, который будет использовать ваш сценарий. Если кто-то сможет предсказать это имя, он сможет оказаться там раньше вас. А это уже, как правило, плохие новости.

В некоторых операционных системах есть библиотечные вызовы, которые генерируют имена временных файлов, используя современный алгоритм случайных значений. Чтобы проверить вашу операционную систему, вы можете запустить следующий код. Если получаемые имена кажутся вам достаточно случайными, вы можете полагаться на POSIX: :tmpnam(). Если нет, вы можете написать собственную функцию генерации случайных имен файлов:

use POSIX qw(Tr4pnar-'):

for (1..20){ print POSIX::tmpnam(),"\n"; }

Как только у вас будет имя файла, которое нельзя отгадать, вам нужно будет открыть его безопасным образом:

sysopen(TEMPFILE,$tmpname,0_RDWR|0_CREATjO_EXCL0666);

Существует другой, более простой способ выполнить эти же два шага (получить имя и открыть временный файл). Метод 10: : File->new_trnpfi-1е() из модуля 10: : File не только подберет хорошее имя (если системные библиотеки это поддерживают), но и откроет файл для чтения и записи.

Примеры использования POSIX: :tmpnam() и 10: : File->new_tmpfile(), a также другую информацию по этой теме вы можете найти в главе 7 книги рецептов «Perl Cookbook» («Perl: библиотека программиста»). В модуле File: :Temp Тима Дженнеса (Tim Jenness) также предпринимаются попытки обеспечить безопасные операции работы с временными файлами.



Содержание раздела