Укрощение @Интернет@

Отсутствие фильтрации


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

open(f,$filename);

while(<f>)

{

print;

}

Ошибки очевидны: во-первых, злоумышленник может получить содержимое любого файла системы, доступного скрипту, передав запрос наподобие "/etc/passwd", а, во-вторых, указав в имени файла символ конвейера ("|"), он сможет запустить любое доступное скрипту приложение и увидеть в браузере результат его работы (например, "echo "+ +" >/.rhosts" позволит подключиться по протоколу rlogin без ввода пароля).

Но не всякая уязвимость так очевидна! Попробуйте найти ошибку в следующей реализации того же примера, усиленного принудительным добавлением расширения ".html" к имени открываемого файла:

open(f,$filename.".html");

while(<f>)

{

print;

}



На первый взгляд, злоумышленник не сможет ни открыть, ни запустить никакие другие файлы, кроме HTML. Но это не так! Дело в том, что ядро Perl не трактует символ нуля как конец строки и обрабатывает его точно так, как и все остальные символы. В то же время, компоненты Perl-а, написанные на Си, интерпретируют ноль как конец строки! Таким образом, для обхода защиты достаточно передать строку, содержащую на конце "\0" (например, "/etc/passwsd\0")! Помимо этого, функция open допускает возможность одновременного запуска множества файлов, - передача строки "|calc.exe|sol.exe|freecell.exe|" приведет к запуску приложений "Калькулятор", "Пасьянс Косынка" и "Пасьянс Свободная ячейка", независимо от того {<<< убрать "?"}будет ли добавлено в конце расширение ".html" или нет.


Даже "open(f, "/home/www/pages/".$filename."html")" не уберегает от использования нескольких символов конвейера, и тем более не предотвращает обращения к вышележащим каталогам, хотя на первый взгляд такая защита может показаться неприступной.

Решение проблемы заключается в фильтрации данных – удалении из ввода пользователя всех потенциально опасных символов или выдачи сообщения об ошибке при их обнаружении. Таких символов довольно много и все они (что очень неприятно) специфичны для каждой функции. Например, у open

опасны следующие символы и их комбинации:

·         ">",">>" и "+>" открытие файла для записи, дозаписи и перезаписи соответственно

·         "+<" открытие файла для записи и чтения

·         "|" и "`" запуск программы

·         "-" чтение со стандартного ввода

·         "&" обращение к файловому дескриптору (handle)

·         ".." и "/" – обращение к вышележащим каталогам

·         "\0" – задание конца строки

О возможности обращения к файлу по его дескриптору следует сказать особо. Пусть существует некоторый секретный файл (например, файл паролей или номеров кредитных карт), который открывается в начале работы программы, а затем на экран выводится содержимое файла, запрошенного пользователем, до закрытия секретного файла. Если злоумышленнику доступен исходный тест скрипта или хотя бы приблизительно известны манеры его разработчика, он сможет прочитать секретный файл с помощью самой программы, передав вместо имени его дескриптор! Для чего достаточно воспользоваться клонированием "x&filehandle" или созданием псевдонимов "x&=filehandle", где "x" обозначает режим доступа – "<" для чтения и ">" для записи. Следующий пример как раз и демонстрирует эту уязвимость.



open (psw,"passwd") || die;   #открытие файла паролей

#...некоторый код...

print "введите имя файла:"    #запрос имени отображаемого файла

$filename=<>; chop $filename;

if ($filename eq "passwd")    #проверка имени на корректность

{print "Hello,Hacker!\n";die;}

open(f,$filename) || die;     #вывод файла на экран

while(<f>)

{

print;

}

Если злоумышленник введет "<&=psw" или "<&psw", в окне собственного браузера он увидит содержимое файла паролей!

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

Много трудностей и непонимания вызывает интерполяция строк, заключенных в двойные кавычки. Язык Perl может автоматически подставлять вместо имени переменной ее содержимое, а вместо имени функции – возвращенный ею результат. Последняя возможность считается особо опасной, т.к. на первый взгляд позволяет злоумышленнику вызывать любые команды Perl и даже выполнять внешние программы с помощью функций exec, eval и многих других. Практически все руководства по написанию скриптов настоятельно рекомендуют фильтровать символы "@", "$", "[]", "{}", "()", и разработчики (даже опытные!) в большинстве своем послушно следуют этому требованию!

На самом деле никакой опасности нет – интерполяция строк выполняется только в текстах программ и никогда в значениях переменных. Наглядно продемонстрировать это утверждение позволяет следующий пример (предполагается, что во втором случае с клавиатуры вводится : "${\(print '>Hello')}"; наклонным шрифтом выделен вывод программы на экран):

$filename="${\(print '>Hello')}";         $filename=<>;



print "$filename";                        print "$filename";

>Hello1                                                                                                 ${\(print '>Hello')}

Замены имени функции на результат ее работы в пользовательском вводе не произошло! Независимо от того, заключена ли введенная строка в двойные кавычки или нет, она всегда отображается на экране такой, какая есть, без каких бы то ни было преобразований. Фильтровать символы интерполяции не нужно – их использование злоумышленником не возымеет никакого эффекта! Тем более, что "собака" является неотъемлемой частью адреса электронной почты и отказ от нее просто невозможен.

Так же напрасны опасения относительно обратной кавычки – "`". В документации по языку Perl сказано, что строка, заключенная в обратные кавычки, интерпретируется как команда операционной системы, которой она и передаются на выполнение. Да, это действительно так, но только по отношению к строкам текста программы, а не содержимому скалярных переменных. Т.е. конструкция "$a=`type /etc/passwd`;" занесет в переменную $a содержимое файла "/etc/passwd", но "$a=<>;" никогда не приведет к подобному результату – чтобы ни ввел пользователь, Поэтому, символ обратной кавычки никакой угрозы не несет и совершенно ни к чему его фильтровать.

Гораздо больше проблем связано с вызовом внешних программ, работающих с данными, введенными пользователем. Заведомо невозможно узнать - какие символы потенциально опасны, а какие нет. Большинство приложений помимо документированных функций имеют множество недокументированных особенностей или хуже того – ошибок реализации.

Никогда нельзя быть абсолютно уверенным, что ваш почтовый агент не воспримет вполне легальный адрес назначения как собственный ключ или управляющее сообщение. Даже если отмахнутся от подобных экзотических угроз, составление списка фильтруемых символов по-прежнему будет представлять проблему, т.к. из документации не всегда бывает ясно как поведет себя приложение, встретив ту или иную комбинацию символов. Помимо явно опасного перенаправления ввода-вывода, вызова конвейера, использования символов-джокеров, символов-разделителей и переноса строк, иногда приходится сталкиваться с такими неожиданными "подлостями" как, например, возможность автоматического развертывания UUE-сообщений.



Лучше всего – полностью отказаться от вызова внешних программ, реализуя все необходимое самостоятельно. Ту же процедуру отправки писем не сложно выполнить и средствами самого языка Perl, без каких либо обращений к SendMail-у или другому МТА, и файлы на диске искать не вызовом grep, а собственноручно написанным модулем. Усложнение программы компенсируется увеличением ее надежности и безопасности.

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


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