Главная страница

WEb практикум. Web'cepbep


Скачать 4.76 Mb.
НазваниеWeb'cepbep
АнкорWEb практикум
Дата22.01.2023
Размер4.76 Mb.
Формат файлаdocx
Имя файлаWEB.docx
ТипДокументы
#898678
страница9 из 18
1   ...   5   6   7   8   9   10   11   12   ...   18

Injection test






form name="syst" action="sys.php" method="get">

Command:










if (isset($_GET['command']))

{

$command = $_GET['command'];

print("Executed command: $command
"); print(system($command));

}

?>







Этот код отображает форму для ввода команды, которая передается функции system. Результат выполнения отображается на web-странице.

Запустите пример, введите в поле ввода команду ОС, на которой у вас установлен web-сервер, и нажмите кнопку Find. Если это Linux или другая UNIX-подобная ОС, то в качестве примера можно ввести команду ls. В результате вы должны увидеть на экране содержимое текущего каталога, например, как показано на рис. 4.1. В мо­ем случае можно увидеть имена каких-то глав, это потому, что я использую сервер, который до этого использовал при написании книги "PHP глазами хакера".

Выполнение системных команд в сценарии всегда связано с повышенным риском, потому что управление правами доступа и фильтрация параметров в этом случае затруднены.

Конечно, такой идеальный вариант, как в листинге 4.1, встретить очень сложно, но все же бывают случаи, когда подобные сценарии администраторы создают для удоб­ства управления web-сервером и надеются, что хакер о них никогда не узнает или не получит к ним доступа (например, если данный код расположен в панели админист­рирования). Намного чаще можно встретить вызов определенной системной коман­ды, а через переменную передается дополнительный параметр. Например: print(system("ping -c 4 ".$command));











• • •

Injection test

х +

-> О A Not Secure phpbook/sys.php?command=ls

■■■ Apps В Work В Dev

О

# * г-щ : В Other Bookmarks

Injection test

Command: I Is

Executed command: Is

Sources.zip

chapter1

chapter2

chapter3

chapters

inc.php

news

paraml.php sys.php sys.php
Рис. 4.1. Пример выполнения системной команды

В этом примере выполняется системная команда Linux ping с параметром -с 4, кото­рая проверяет связь с указанным компьютером. Параметр -с используется для ука­зания количества попыток или посылаемых пакетов, в других ОС он может быть другим: например, в Windows для этой цели используется -n. В данном случае мы ограничиваемся пятью пакетами. А вот адрес компьютера указывается через пере­менную $command, которая содержит переданные пользователем сценарию данные. Пользователь должен ввести в форму адрес компьютера, с которым необходимо проверить связь, и в результате мы увидим то, что показано на рис. 4.2.

На первый взгляд все хорошо и безопасно. Но это только если пользователь будет указывать IP-адрес или имя компьютера и ничего другого. А что другое может пе­редать злоумышленник? Например, другую команду. Дело в том, что ОС Linux мо­жет выполнять несколько команд подряд. Вы одной строкой можете передать две и более команды, поставив между ними в качестве разделителя точку с запятой. На­пример, таким образом можно выполнить две команды — ping и просмотр текуще­го каталога:

ping flenov.info -с 5; ls -al

Эта строка будет разбита на две команды: ping -с 5 flenov.info и ls -al. И обе эти команды будут выполнены системой, а результат вы увидите на экране.

Если у вас есть web-сервер, на котором можно протестировать PHP-сценарии, то загрузите туда файл из листинга 4.1 и попробуйте передать через поля ввода строку "flenov.info; ls -al". Будут выполнены обе команды, и результат обеих будет отображаться на web-странице. На рис. 4.3 показан пример выполнения такой ко­манды, только я выполнял ping на одной из машин в своей сети с адресом

192.168.1.1. В черном прямоугольнике показан результат выполнения команды ls, а без выделения прямоугольником — результат ping.


Г

® ^ Injection test X -f*




-> G A Not Secure phpbook/sys.php?command=ping+...

0 #> * ;

|i Apps В Work В Oev

В Other Bookmarks

Injection test

Command: ping flenov.info -c 4 | Find

Executed command: ping flenov.info -c 4




PING flenov.info (162.241.30.65): 56 data bytes

64 bytes from 162.241.30.65: icmp_seq=0 ttl=48 time=99.650 ms

64 bytes from 162.241.30.65: icmp_seq=l ttl=48 time-140.875 ms

64 bytes from 162.241.30.65: icmp_seq=2 ttl=48 time=152.985 ms

64 bytes from 162.241.30.65: icmp_seq=3 ttl=48 time-160.148 ms




flenov.info ping statistics

4 packets transmitted/ 4 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 99.650/138.415/160.148/23.417 ms round-trip min/avg/max/stddev = 99.650/138.415/160.148/23.417 ms










Рис. 4.2. Результат выполнения команды ping

WEB'CEPBEP 1

глазами 1

Оглавление 4

Введение 8

Основы безопасности 12

1.3.1.Определение типа операционной системы 19

1.3.2.Определение имен работающих служб 20

1.3.3.Используемые фреймворки 24

1.3.4.Использование эксплоитов 28

1.3.5.Автоматизация 29

1.4.1.Анализатор web-уязвимостей 33

1.4.2.Взлом с помощью поисковой системы 36

1.7.1.Distributed Denial of Service (DDoS) 46

1.7.2.Защита от распределенной атаки 47

1.8.1.Защита web-сервера 49

1.8.2.Модули безопасности Apache 50

1.9.1.Права сценариев web-сервера 52

1.9.2.Права системных сценариев 53

1.9.3.Права доступа к СУБД 54

1.11.1.Самостоятельно написанные программы 58

1.11.2.Готовые решения 59

1.11.3.Программы, написанные под заказ 60

1.11.4.Золотая середина 60

Простые методы взлома 64

2.1.1.Вариант накрутки № 1 64

2.1.2.Вариант накрутки № 2 65

2.1.3.Вариант накрутки № 3 66

2.1.4.Защита от накрутки 67

2.3.1.Внутренний мир каптчи 71

2.3.2.Примеры некорректных каптчей 73

2.3.3.Пример хорошей каптчи 74

Взлом PHP-сценариев 80

3.1.1.Пример реальной ошибки 80

3.1.2.Проблема include 85

3.1.3.Инъекция кода 89

3.2.1.Лишние сценарии на рабочем сервере 91

3.2.2.Дополнительные программы 91

3.2.3.Резервные копии или старые файлы 92

3.3.1.Метод GET 96

3.3.2.Метод POST 98

3.3.3.Уязвимость 101

3.3.4.Другие методы 103

3.3.5.Инициализация переменных 104

3.4.1.Конфигурационные файлы 110

3.4.2.Промежуточные модули 113

3.4.3.Скрытые функции 116

3.9.1.Воровство кликов 125

3.9.2.Cross Frame Scripting 125

3.9.3.Защита от фреймов 126

Работа 130

с системными командами 130

• • • 131

4.3.1.Проверка корректности файлов изображений 142

4.3.2.Проверка корректности текстовых файлов 144

4.3.3.Сохранение файлов в базе данных 145

4.3.4.Обращение к файловой системе 145

4.3.5.Угроза безопасности 148

SQL-инъекция (PHP + MySQL) 149

5.2.1.Сбор информации 156

5.2.2.Использование уязвимости 170

5.2.3.Доступ к файловой системе 172

5.2.4.Поиск уязвимости 172

5.2.5.Процент опасности 173

5.2.6.Возможные проблемы 176

5.2.7.От теории к практике 178

SQL-инъекция .NET + MS SQL Server 181

6.1.1.Опасные процедуры MS SQL Server 181

6.1.2.Распределение прав доступа 184

6.1.3.Опасные SQL-запросы 186

6.1.4.Рекомендации по безопасности MS SQL Server 187

CSRF, или XSRF-уязвимость 192

DoS-атака на web-сайт 201

8.2.1.Оптимизация SQL-запросов 202

8.2.2.Оптимизация базы данных 208

8.2.3.Выборка необходимых данных 211

8.2.4.Резюме 212

8.3.1.Кеширование вывода 213

8.3.2.Кеширование web-страниц 214

8.3.3.Программные решения 216

8.3.4.Медленный код 217

8.3.5.Асинхронный код 218

Авторизация 226

XSS 239

Заключение 251

Предметный указатель 253

Sources.zip chapter1

chapter2 chapter3 chapters inc.php news

paraml.php sys.php sys.php

А если передать строку "fienov.info; cat /etc/passwd", то в результате вы увидите на экране содержимое файла со списком пользователей, конечно, если у вас будет достаточно прав на доступ к этому файлу.

Получается, что такая безобидная ситуация может привести к серьезным проблемам, ведь с помощью команд ОС хакер легко может выполнить такие операции, как про­смотр системных файлов. Такие обращения опасны вне зависимости от используемо­го языка программирования. При вызове системных команд хакер использует воз­можности ОС, а не языка программирования, на котором написаны сценарии. Про­граммист должен обеспечить проверку получаемых от пользователя данных и обезопасить систему от вторжения. Не забывайте, что из-за одного сценария может пострадать не только определенный web-сайт, но и все web-сайты, расположенные на том же web-сервере, если права доступа настроены неверно.

В PHP есть одна интересная функция, которая упрощает жизнь в нашей борьбе за безопасный код: EscapeShellcmd. Она удаляет из переменной любые символы, кото­рые могут быть использованы в командном интерпретаторе как произвольные ко­манды. Это значит, что если необходимо передать переменную в функцию exec, system и ей подобную, то предварительно необходимо проверить эту переменную с помощью EscapeShellcmd. Например:

$cmd = EscapeShellCmd($cmd); system($cmd) ;

В первой строке мы убираем из переменной $cmd все, что может представлять опас­ность, а после этого уже переменная передается в функцию system.

Да, функция EscapeShellcmd — хороший инструмент, но полностью полагаться на нее не стоит. Я рекомендую выполнять дополнительную проверку с помощью ре­гулярных выражений.

  1. Защита от выполнения произвольных команд

Любая переменная, которая выполняется системой или внешней утилитой, должна проверяться от и до. Как минимум, никаких точек с запятой в ней содержаться не должно. Но одной только этой проверки мало. Проверяемые символы зависят от ситуации. Например, если из переменной $command вырезать только точки с запятой, то это не значит, что сценарий неуязвим и хакер уже ничего не сможет сделать. В поле все еще остается возможность влиять на команду ping, указывая свои ключи. Хакер может указать помимо IP-адреса бесконечное количество и большой размер пакета. Запустив несколько копий сеансов, можно таким образом организовать DoS-атаку на небольшой сервер, подключенный через узкий канал связи.

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

[a-zA-Z0-9\._\-]+(\.[a-zA-Z0-9]+)+)*$

Если параметр не соответствует формату имени домена, то результатом проверки будет ошибка, в этом случае сценарий не должен передавать данные системному интерпретатору команд.

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

Л([a-zA-Z0-9\._\-]+@[a-zA-Z0-9\._\-]+(\.[a-zA-Z0-9]+)+)*$

Это регулярное выражение похоже на предыдущее, но имеются и небольшие отли­чия. Давайте разберем его по частям:

  • Л ([a-zA-z0-9\._-] + — в начале строки до символа @ идет имя пользователя поч­тового ящика. Здесь могут быть любые разрешенные символы, но при этом их не должно быть меньше одного, поэтому в конце присутствует символ +;

  • [a-zA-zo-9\._-] + — имя почтового сервера, которое идет после символа @, под­чиняется тем же правилам, что и имя пользователя, поэтому эта часть соответст­вует предыдущей;

  • (\.[ a-zA-zo-9]+)*$ — имя домена. Здесь могут быть цифры и буквы. В интернете мы работаем с буквенными именами доменов, но в локальных сетях иногда встречаются и цифровые. При этом имя домена является не обязатель­ным, о чем говорит символ *.

Как видите, все достаточно просто. Главное, правильно написать регулярное выра­жение, и вы избавите себя от множества проблем. Но не забывайте тщательно тес­тировать свой сценарий, потому что ошибиться в такой конструкции достаточно легко, а найти ошибку — проблематично.

Возможно, не все языки программирования поддерживают регулярные выражения (я все языки не знаю) или вы боитесь сделать ошибку. В этом случае вы можете вос­пользоваться простыми функциями поиска внутри строки и замены, удаления опас­ных символов или просто предупреждать пользователя о возможных проблемах.

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

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

  1. Загрузка файлов

Не буду говорить, что загрузка файлов опасна, а лучше скажу, что она сверхопасна. Задача программиста — ограничить возможность загрузки данных так, чтобы хакер не смог загрузить ничего, что он сможет использовать для взлома.

Как и любая другая отправка данных, файлы могут передаваться web-серверу мето­дами put или post. Метод post мы уже рассматривали (см. разд. 3.3.2), а вот put по­ка еще нет.

Теперь посмотрим, как работает данный метод. Начнем с HTML-формы для от­правки:



Файлы для отправки







В свойствах формы помимо action с указанием сценария обработки и метода от­правки необходимо указать свойство enctype, которое определяет, в какой коди­ровке должны отправляться данные. По умолчанию оно содержит application/x- www-form-urlencoded, то есть кодировку формы. Но для файлов, особенно бинарных, необходимо изменить это свойство на multipart/form-data.

Для ввода имени файла используется элемент управления input. Для удобства тип (свойство type) этого элемента равен file. Этот тип поддерживается большинством браузеров и отображает на экране не только поле ввода, но и кнопку для выбора файла. По нажатию этой кнопки на экране будет появляться стандартное окно вы­бора файла (рис. 4.4).







А как будет происходить сама передача данных? Браузер не может создать соедине­ние с файлом сценария и передавать ему данные. Это будет слишком сложно, а в не­которых случаях просто невозможно. Но было найдено великолепное решение: после нажатия кнопки отправки данных файл загружается во временный каталог, и только тогда запускается указанный сценарий.

Из сценария мы можем увидеть данные файла через массив $http_post_files. Этот массив является двумерным. Первый уровень определяет имена полей, в которых находятся параметры файла. Одна форма может отправлять несколько файлов, поэтому $нттp_POST_FILES[поле] указывает на нужный нам файл. В пред­ставленной выше форме поле для ввода имени файла имеет имя filel, поэтому из сценария к этому файлу обращаемся так:

$HTTP_POST_FILES["file1"]

Второй уровень определяет свойства загруженного файла, здесь есть следующие элементы массива:

  • tmp_name — имя временного файла;

  • name — имя файла источника на машине клиента;

  • type — тип файла;

  • size — размер файла.

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

$PHP_POST_FILES["filel"]["tmp_name"]

Но это еще не все. Временный файл на UNIX-серверах чаще всего создается в ката­логе /tmp. Это общедоступный каталог, куда могут писать все пользователи, что не очень хорошо. К тому же файл, содержащийся в этой папке, может быть удален системой. Намного эффективнее будет в сценарии учесть этот момент и скопиро­вать файл в специально выделенный для данных целей каталог.

Следующий пример показывает, как можно получить файл от пользователя, скопи­ровать его в каталог /var/www/html/files и отобразить информацию о нем:


print("
File Size: %s", $HTTP_POST_FILES["file1"]["size"]); print("
File Type: %s", $HTTP_POST_FILES["file1"]["type"]); print("
File Name: %s", $HTTP_POST_FILES["file1"]["name"]); print("
Temp File Name: %s",

$HTTP_POST_FILES["file1"]["tmp_name"]);

if (copy($PHP_POST_FILES["file1"]["tmp_name"],

"/var/www/html/files/".$HTTP_POST_FILES["file1"]["name"])) print("Копирование завершено"); else

print("Ошибка копирования файла 1
");

?>

Загрузка может стать невозможной, если она отключена в конфигурационном фай­ле php.ini. Откройте этот файл и проверьте директиву file_uploads. Чтобы иметь возможность загружать файлы на web-сервер, этот параметр должен быть включен (on). Если ваши сценарии не используют загрузку, то убедитесь, что этот параметр выключен (off).

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

Если вы используете глобальные переменные (для этого в конфигурационном фай­ле директива register_globals должна быть включена (on)), то к параметрам загру­женного файла можно получить доступ через следующие переменные:

  • $fiiei — имя временного файла;

  • $filel_name — имя загруженного файла;

  • $fiie 1 s ize — размер файла;

  • $file1_type — тип файла.

В итоге код загрузки может выглядеть следующим образом:


print("
Temp file name $file1"); print("
File name $file1_name"); print("
File size $file1_size"); print("
File type $file1_type");

if (copy($file1,

"/var/www/html/files/".$file1_name)) print("
Complete"); else

print("Ошибка копирования файла 1
");

?>

Прежде чем работать с файлом через автоматически зарегистрированные переменные, вы должны проверить, данный файл действительно загружен или данная переменная была просто создана в ответ на параметр из строки URL. Для этого нужно воспользо­ваться параметром file_name. Это очень важно, ведь если указать URL http://servername/upload?file_name=../../../../../../etc/passwd, то сценарий скопирует файл паролей, что в дальнейшем позволит его просмотреть.

Попробуйте выполнить сценарий и посмотреть на имя временного файла. Я загрузил файл file.txt, а временный файл получил имя /tmp/phpmI1XXc. Web- сервер изменяет имя по своему усмотрению, поэтому его нужно брать из параметра $file1_name, т. е. реального имени файла, переданного от клиента.

Теперь посмотрим права доступа загруженного файла. Сделаем это с помощью ftp- клиента, я буду использовать бесплатный Cyber Duck. Подключаемся к серверу, вы­деляем загруженный файл, вызываем контекстное меню и выбираем в нем Info. Об­ратите внимание, что разрешено практически все (рис. 4.5). В ОС Linux есть воз­можность указать, с какими правами по умолчанию будут создаваться файлы. Нужно изменить это значение до минимально необходимого или сразу после за­грузки менять в своем коде — на мой взгляд, первое предпочтительнее.

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

О правах доступа в Linux можно прочитать в моей книге "Linux глазами хакера".


# # Info - Permissions - •

“4

4103-LenovoYoga9x.jpg



/



Щ

General Permissions

Metadata

Distribution (CDN)

SFTP

UNIX Permissions

757







Owner

Read

Write

Я Execute

Group

Read

Write

Execute

Others

Я Read

Я Write

Я Execute

L

?

} Apply changes recursively

j

Рис. 4.5. Права доступа на загруженный файл





Рассмотрим еще одну классическую задачу — ограничение размера загружаемого файла. Это можно сделать тремя способами: с помощью ограничения размера фай­ла в HTML-форме, на стороне web-сервера и с помощью параметра конфигураци­онного файла. Для ограничения в HTML-форме необходимо добавить невидимое поле с именем max_file_size, а в качестве значения указать нужный размер:



В данном примере в качестве максимального размера указано значение 300 байтов. Файлы большего размера загружаться не будут. Эта строка должна идти до описа­ния поля ввода имени файла. В результате форма отправки файла будет выглядеть следующим образом:



Отправка файлов:











Попробуйте теперь загрузить файл меньше разрешенного размера. Управление бу­дет передано сценарию, но при этом файл не будет загружен, поэтому параметр размера файла $file1_size будет равен нулю, а имя временного файла $file1_size будет равно none. Таким образом, в сценарии желательно добавить проверку, преж­де чем производить копирование: if ($file1=="none") dieC^^^ слишком большой");

Проблема этого метода заключается в том, что хакер может легко удалить невиди­мое поле с ограничением, ведь проверка будет происходить на стороне клиента в HTML-форме. Поэтому лучше производить проверку на стороне web-сервера, когда файл будет загружен:


if ($file1_size>10*1024) dieC'CnMUKQM большой размер файла");

if (copy($file1,

"/var/www/html/files/".$file1_name)) print("
Complete"); else

printC^m^^ копирования файла 1
");

?>

В этом примере мы в начале сценария проверяем, чтобы размер загруженного фай­ла не был более 10 Кбайт (10 х 1024 байта, т. е. 1 Кбайт). Преимущество этого мето­да: проверку невозможно убрать, если не иметь доступа к исходному коду сценария и возможности его редактирования.

Последний способ: изменение директивы upload_max_filesize конфигурационного файла php.ini. Следующий пример устанавливает максимальный размер в 2 Мбайта:

upload_max_filesize = 2M

Недостатком всех методов проверки на web-сервере является то, что если пользова­тель выберет слишком большой файл, то он будет загружен, и только после этого станет известно, что размер слишком большой. Это трата трафика и времени, что может не понравиться добропорядочным пользователям вашего web-сайта. Чтобы экономить трафик, делайте проверку и на web-сервере (для защиты от хакеров), и на стороне клиента.

  1. Проверка корректности файлов изображений

Ранее мы выяснили, что загружаемые файлы подвержены проблеме прав доступа. Чтобы файл можно было загрузить, для каталога должны быть установлены права на запись для всех пользователей. Загруженный файл по умолчанию может иметь права на выполнение. Таким образом, если хакер загрузит свой сценарий и сможет выполнить его на web-сервере, то это уже взлом.

Чтобы хакер не смог загрузить ничего опасного, необходимо следить за содержимым закачиваемых данных. Очень часто на web-сайтах используется возможность закачки файлов изображений. Например, на форуме может предоставляться возможность поль­зователям закачивать свои изображения, которые будут отображаться над сообще­ниями. Но при этом мы должны быть уверенными, что в файле действительно гра­фические данные, а не текст и, тем более, не сценарий. Как в этом убедиться?

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

Попробуйте закачать GIF-файл на web-сервер с помощью рассмотренного ранее сценария (см. разд. 4.3). В поле type будет содержаться текст "image/gif". До знака "слеш" находится тип image, а после можно увидеть расширение файла. Для JPEG- файлов после слеша можно увидеть jpg, jpeg или pjpeg, а для PNG-файла это будет png. Все эти типы поддерживаются большинством современных браузеров, и имен­но их мы будем проверять.

А теперь попробуем переименовать простой текстовый файл со сценарием в файл с расширением gif и закачаем его тем же сценарием. В переменной типа бу­дет содержаться текст "plain/text". Несмотря на расширение графического файла, программа видит, что тип файла — текстовый. Получается, что нельзя верить рас­ширению, а вот типу файла можно доверять, только осторожно.

В листинге 4.2 приведен пример сценария, в котором тип файла разбирается на со­ставляющие (до и после слеша) и происходит проверка достоверности расширения и типа. Для разбиения текста типа файла удачно подходит функция preg_match с ре­гулярным выражением ' ([a-z]+) \/ [x\-]* ([a-z] +) '. Затем происходит проверка с помощью оператора switch полученного типа. Если он не соответствует ни одному из разрешенных, то вызывается метод die для завершения работы сценария.

Листинг 4.2. Проверка корректности


preg_match("'([a-z]+)\/[x\-]*([a-z]+)'", $file1_type, $ext); print("
$ext[1]"); print("
$ext[2]");

switch($ext[2])

{

case "jpg": case "jpeg": case "pjpeg": case "gif": case "png": break; default:

die("
This is not image");

}

if (copy($file1,"/var/www/html/files/".$file1_name)) print("
Complete"); else

print("
Error copy file 1
");

?>

Но этого недостаточно. Не помешает до копирования файла проверить размер изо­бражения. Даже если у вас нет ограничения на размер файла, следует вызвать функцию getimagesize. Этой функции передается путь к файлу, а в результате мы получаем размер находящегося в нем изображения. Если в момент определения размеров произошла ошибка, то перед нами не графический файл, а обман. Сле­дующий пример показывает, как можно выполнить проверку корректности файла через определение размера изображения:

$im_prop=getimagesize($file1); print("
$im_prop[0]x$im_prop[1]"); if ($im_prop[0]>0)

{

if (copy($file1,"/var/www/html/files/".$file1_name)) print("
Complete"); else

print("
Error copy file 1
");

}

else

die("Image size error")

Функция getimagesize возвращает массив из свойств графического файла. В этом массиве элементы нумеруются с нуля и содержат следующее:

  • ширину изображения;

  • высоту изображения;

  • тип, где 1 — GIF, 2 — JPG, 3 — PNG, 4 — SWF, 5 — PSD, 6 — BMP, 7 — TIFF (фор­мат Intel), 8 — TIFF (формат Motorola), 9 — JPC, 10 — JP2, 11 — JPX;

  • строку вида: height="yyy" width="xxx".

Таким образом, нам нужно проверить: если ширина или высота равны нулю, то это не изображение или его формат просто неизвестен функции getimagesize. Изображе­ние не должно иметь ширину или высоту, равную нулю, иначе оно бессмысленно или содержит некорректные данные.

Двойная проверка более надежна, но не является решением всех проблем. Напри­мер, несколько лет назад была найдена ошибка в функциях определения размера изображения в PHP. Да, функций несколько. Для каждого типа файла своя функ­ция, но все они объединены в одну getimagesize. Если сценарию передать графиче­ский файл TIFF, в котором будет указан размер -8, то сценарий попадает в беско­нечный цикл и будет выполняться, пока не поглотит все ресурсы сервера. Таким образом, один хакер может без проблем произвести DoS-атаку. Ошибки находили и в функции обработки JPEG-файла.

Я понимаю, что ошибка в функции getimagesize — это проблема разработчиков PHP, а не нашего сценария. Но проблема есть, и закрывать на нее глаза нельзя, по­этому наша задача — следить за безопасностью не только сценариев, но и исполь­зуемых программ и своевременно их обновлять. Каким бы ни был безопасным сце­нарий, проблемы могут настигнуть нас с других сторон.

  1. Проверка корректности текстовых файлов

Теперь поговорим о загрузке текстовых файлов. Например, на форуме можно пре­доставить программистам возможность обмениваться файлами с примерами кода. Если хакер имеет возможность загрузить файл на web-сервер, и этому файлу уста­навливаются права на выполнение, то помимо текста хакер может загрузить необ­ходимый для взлома сценарий. После этого остается только узнать, куда был за­гружен файл, и выполнить его.

Даже если файл не имеет прав на выполнение, но подключается сценарием с помощью функции include или require, то, какое бы ни было у файла расшире­ние, PHP-код в нем будет выполняться.

Хакер также может использовать загрузку и для преследования следующих коры­стных целей:

  • использовать ваш web-сервер для хранения собственных файлов, пиратских про­грамм и других данных. Чтобы избежать этого, можно ограничить загружаемый файл в размере;

  • дисковое пространство не бесконечно, поэтому хакер может загрузить на web- сервер множество файлов, и через какое-то время диск может быть переполнен­ным, а web-сервер начнет работать с перебоями или выйдет из строя.

А как же тогда хранилища файлов, которые существуют в интернете, как же они не боятся того, что их взломают?

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

Предупрежден — значит вооружен, так что, зная, откуда может прийти беда, впол­не реально построить защиту.

  1. Сохранение файлов в базе данных

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

А что если сохранять файлы в базе данных? Тогда не будет обращения к файловой системе, а значит, не будет вероятности увидеть или перезаписать системные фай­лы. Но не забываем, что загрузка происходит во временный каталог, а уже оттуда в базу данных. Но в целом это может быть неплохой вариант защиты, если файл со­храняется в базу как бинарный.

Если происходит загрузка какого-то небольшого текстового файла, который сохра­няется в простое текстовое поле, то тут есть вероятность столкнуться с уязвимо­стью SQL Injection, о которой мы будет говорить в главе 6.

  1. Обращение к файловой системе

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

Листинг 4.3. Чтение файла







Inj ection test






form name="syst" action="file1.php" method="get"> Command:









if (isset($_GET['command']))

{

if ($arr=file($_GET['command']))

{

for ($i=0; $i
{

printf("
%s", $arr[$i]);

}

}

}

?>





Параметр, через который передается имя файла, никак не проверяется на допусти­мые символы, а значит, хакер сможет просмотреть любой файл, на чтение которого у него хватит прав доступа. Просматривая конфигурационные файлы, хакер может найти важные данные, а возможно, и пароли доступа к web-серверу (см. главу 3). Ошибка очень похожа на проблему include (см. разд. 3.1.2), разница только в том, что в данном случае вы можете только просмотреть файлы, но код в них не будет выполняться, как при подключении.

Некоторые программисты предпочитают использовать вместо include простое чте­ние файлов. Согласен и поддерживаю это решение. Если файл не содержит PHP- кода, а только HTML, то его лучше прочитать и отобразить на web-странице. Та­ким образом, даже если вы забудете где-то проверить параметр, хакер сможет толь­ко просматривать файлы, но не сможет внедрить свой PHP-код в сценарий, что на­много опаснее. Код выполняться не будет, но просмотреть важную информацию будет все еще возможно, например увидеть /etc/passwd.

Если полученное имя без проверок используется при записи файла, то тут уже со­всем иное. Допустим, что нам необходим сценарий, что-то вроде гостевой книги или книги отзывов. В зависимости от типа вопроса пользователя мы должны будем поместить введенный вопрос в тот или иной файл. Имя файла и сохраняемые дан­ные будем передавать методом get, хотя и другой метод передачи защищенным назвать нельзя. Упрощенный вариант решения данной задачи можно увидеть в лис­тинге 4.4.

Листинг 4.4. Сохранение введенных пользователем данных







Inj ection test






form action="file.php?command=questions/user.txt" method="get">
Question:









if (isset($_GET['command']))

{

if ($f=fopen($_GET['command'] , "w+")) print("File: ($f)");

else

die("Error");

$s = fwrite($f, $_GET['param']); fclose($f);

}

?>





Смысл сценария прост. В свойстве формы action указывается URL: file.php?command=questions/user.txt. Помимо сценария в URL указан еще и пара­метр command, в котором прописано имя файла для записи данных. Несмотря на то, что параметр прописан, изменить его очень просто. Через параметр param передается текст, который нужно записать в файл. Если файл главной web-страницы имеет имя index.php и доступен на запись, то можно выполнить следующий URL: http://servername/file.php?command=mdex.php¶m=
1   ...   5   6   7   8   9   10   11   12   ...   18


написать администратору сайта