Отображение произвольных файлов сервера на стороне клиента

otobrazhenie proizvolnyh fajlov servera na storone klienta Сценарии на стороне сервера

Почти сразу после того, как я написал сценарий просмотра исходного программного кода, представленный в предыдущем примере, мне пришло в голову, что, приложив совсем немного усилий, я смогу написать более обобщенную версию, которая принесет больше пользы, — такую, которая по переданному ей имени файла отобразит лю бой файл на сайте. На стороне сервера это простое видоизменение: необходимо лишь разрешить передачу имени файла в качестве входных данных. Сценарий Python getfile.py, представленный в примере 15.27, реализует это обобщение. Он предполагает, что имя файла введено в форму на вебстранице или дописано в конец URL в качестве параметра. Напомню, что модуль Python cgi прозрачным образом обрабатывает оба эти случая, поэтому данный сценарий не содержит программного кода, который как-то разделял бы эти варианты.

Пример 15.27. PP4E\Internet\Web\cgi-bin\getfile.py

#!/usr/bin/python """

############################################################################ Отображает содержимое любого сценария CGI (или другого файла), имеющегося на стороне сервера, не выполняя его. Имя файла можно передать в параметре строки URL или с помощью поля формы (используйте имя сервера "localhost", если используется локальный сервер):

http://servername/cgibin/getfile.py?filename=somefile.htmlhttp://servername/cgibin/getfile.py?filename=cgibin\somefile.py http://servername/cgibin/getfile.py?filename=cgibin%2Fsomefile.py

Пользователи могут сохранить файл у себя, скопировав текст через буфер обмена или воспользовавшись пунктом меню "View Source" ("Исходный код страницы" или "Просмотр HTML-кода"). При обращении к этому сценарию из IE для получения версии text/plain (formatted=False) может запускаться программа Блокнот (Notepad), при этом не всегда используются символы конца строки в стиле DOS; Netscape, напротив, корректно отображает текст на странице броузера. Отправка файла в версии text/HTML действует в обоих типах броузеров текст правильно отображается на странице ответа в броузере.

Мы также проверяем имя файла, чтобы избежать отображения закрытых файлов; в целом это может не предотвратить доступ к таким файлам: не устанавливайте этот сценарий, если исходные тексты закрытых сценариев у вас не защищены каким-то иным способом!

############################################################################

import cgi, os, sys

formatted = True # Тгие=обернуть текст в HTML

privates = [‘PyMailCgi/cgi-bin/secret.py’] # эти файлы не показывать

try:

samefile = os.path.samefile # проверка устройства, номера inode

except:

def samefile(path1, path2): # не доступна в Windows

apath1 = os.path.abspath(path1).lower() # близкая аппроксимация apath2 = os.path.abspath(path2).lower() # нормализовать пути, return apath1 == apath2 # привести к одному регистру


if not formatted:

print(‘Content-type: text/plain\n’) # отправить простой текст

print(filetext) # действует в NS, но не в IE?

else:

print(‘Content-type: text/html\n’) # обернуть в HTML

print(html % (filename, cgi.escape(filetext)))

Этот сценарий на языке Python, выполняемый на сервере, просто извлекает имя файла из объекта с входными данными, читает и выводит текст файла для отправки броузеру клиента. В зависимости от значения глобальной переменной formatted, файл отправляется либо в режиме простого текста (с указанием типа text/plain в заголовке ответа), либо обертывает его разметкой HTML страницы (text/html).

Оба режима (а также другие) в целом корректно интерпретируются большинством броузеров, но Internet Explorer обрабатывает режим простого текста не так элегантно, как это делает Netscape, — при проверке он открыл загруженный текст в редакторе Блокнот (Notepad), но из-за символов конца строки в стиле Unix файл был показан как одна длинная строка. (Netscape правильно выводит текст в теле самой веб-страницы ответа.) Режим отображения HTML в современных броузерах работает более переносимым образом. О логике этого сценария в работе с файлами, доступ к которым ограничен, будет рассказываться несколько ниже.

Запустим сценарий, набрав его URL в адресной строке броузера вместе с именем нужного файла в конце. На рис. 15.28 показана страница, которая была получена при посещении этого адреса URL (вторая ссылка на файл с исходным программным кодом в странице выбора языка, представленной в примере 15.17, имеет аналогичный эффект, но возвращает другой файл):

http://localhost/cgi-bin/getfile.py?filename=cgi-bin\languages-src.py

Рис. 15.28. Универсальная страница просмотра исходного программного кода

В теле этой страницы показан текст находящегося на сервере файла, имя которого было передано в конце URL. После получения файла можно просматривать его текст, выполнять операции удаления и вставки для сохранения в файле на стороне клиента и так далее. В действительности теперь, имея такую обобщенную программу просмотра содержимого файлов, можно заменить гиперссылку на сценарий langu ages-src.py в language.html на адрес URL следующего вида (я включил обе для иллюстрации):

http://localhost/cgibin/getfile.py?filename=cgibin\languages.py

Тонкое замечание: обратите внимание, что в параметре запроса в этой и в других строках URL в данной книге в качестве разделителя каталогов в путях используется символ обратного слэша в стиле Windows. В Windows при использовании локального веб-сервера из примера 15.1 и Internet Explorer мы можем использовать любую из первых двух экранированных форм URL, которые показаны ниже, но попытка использовать литерал символа слэша, как в последней строке, приведет к неудаче (в URL экранированная последовательность %5C соответствует символу \, а %2F — /):

http://localhost/cgibin/getfile.py?filename=cgibin%5Clanguages.py

работает http://localhost/cgibin/getfile.py?filename=cgibin%2Flanguages.py работает http://localhost/cgibin/getfile.py?filename=cgibin/languages.py неудача

Этим данная версия сценария отличается от версии в предыдущем издании этой книги (где для переносимости использовалась последняя форма URL), и, возможно, данное решение не является идеальным (хотя, как и контекст рабочего каталога, это одна из многих особенностей, отличающих серверы и платформы, с которыми вы наверняка столкнетесь в Веб). Проблема, похоже, заключается в том, что функция quote из модуля urllib.parse считает символ / безопасным, а quote_plus — нет. Если вы заинтересованы в обеспечении переносимости строк URL в этом контексте, то вторая форма из трех приведенных является, вероятно, самой лучшей, однако она сложна для запоминания, если придется вводить ее вручную (инструменты экранирования могут автоматизировать преобразование в эту форму). В противном случае вы можете вводить двойные символы обратного слэша, чтобы избежать конфликтов с другими экранированными последовательностями в строках, обусловленными особенностями обработки параметров URL; смотрите ссылки на этот сценарий в примере 15.20, где предпринята попытка исключить конфликт со специальным символом \f.

С более общей точки зрения, такие адреса URL в действительности являются непосредственными вызовами (хотя и через веб) нашего сценария Python с явным параметром, содержащим имя файла. Мы используем сценарий как своеобразную функцию, находящуюся где-то в киберпространстве, которая возвращает текстовое содержимое файла. Как мы видели, параметры, которые передаются в адресе URL, обрабатываются так же, как данные, введенные в поля формы. Давайте для удобства напишем простую веб-страницу, позволяющую ввести имя нужного файла прямо в форме, как показано в примере 15.28.

Пример 15.28. PP4E\Internet\Web\getfile.html

<html><title>Getfile: download page</title>

<body>

<form method=get action="cgi-bin/getfile.py">

<  h1>Type name of server file to be viewed</h1>

<  p><input type=text size=50 name=filename>

<  p><input type=submit value=Download>

</form>

<hr><a href="cgi-bin/getfile.py?filename=cgi-bin\getfile.py">View script code</a>

</body></html>

На рис. 15.29 показана страница, полученная при посещении URL этого сценария. На этой странице требуется ввести только имя файла, а не полный адрес сценария CGI. Обратите внимание, что здесь есть возможность использовать символы слэша, потому что броузер будет экранировать их при передаче запроса, а функция Python open принимает любые типы слэшей в Windows (при создании параметров запроса вручную ответственность за правильность выбора ложится на плечи пользователя или сценария, генерирующего эти параметры).

Если для отправки формы нажать кнопку Download (Загрузить) на этой странице, имя файла передается на сервер, и мы получаем ту же страницу, что и раньше, когда имя файла добавлялось в адрес URL (как на рис. 15.28, хотя и с другим символом-разделителем каталогов). На самом деле имя файла здесь тоже будет добавлено в адрес URL: метод get в разметке HTML формы указывает броузеру, что он должен добавить имя файла в конец URL, как мы делали это вручную. Оно появляется в конце адреса URL, в строке адреса страницы ответа, хотя в действительности мы ввели его в форму. Щелчок на ссылке внизу страницы на рис. 15.29 открывает исходный программный код самого сценария, возвращающего файлы, правда при этом адрес URL определяется явно.

Рис. 15.29. Страница выбора файла для просмотра его содержимого

 

Обработка закрытых файлов и ошибок

Если сценарий CGI обладает правами, достаточными для открытия нужного файла на сервере, этот сценарий можно использовать для просмотра лю бо го файла на сервере и сохранения его на локальном компьютере. Например, на рис. 15.30 изображена страница, полученная при запросе файла PyMailCgi/pymailcgi.html — текстового файла HTML в подкаталоге другого приложения, находящегося в каталоге, родительском для данного сценария (приложение PyMailCGI мы будем исследовать в следующей главе). Пользователи могут указывать как относительные, так и абсолютные пути к файлам — годится любой синтаксис пути, понятный серверу.

Вообще говоря, этот сценарий отобразит файл с любым путем, доступный для чтения пользователю, с привилегиями которого выполняется сценарий CGI. На некоторых серверах для этого обычно используется предопределенная учетная запись «nobody» с ограниченными привилегиями. Это касается почти любого файла на сервере, используемого в веб-приложениях, иначе они оказались бы недоступными для броузеров. При использовании нашего локального веб-сервера можно получить содержимое любого файла, имеющегося на компьютере: например, я смог получить файл C:\Users\mark\Stuff\Websites\public_html\ in dex.html, находящийся на моем ноутбуке, введя путь к нему в форму на рис. 15.29.

Рис. 15.30. Просмотр файлов по относительному пути

 

Усиливая гибкость инструмента, это также представляет потенциальную опасность, если сервер выполняется на удаленном компьютере. Как быть, если потребуется закрыть для пользователей доступ к некоторым файлам на сервере? Например, в следующей главе будет реализован модуль для шифрования паролей учетных записей электронной почты. На сервере он будет находиться по адресу PyMailCgi/cgi-bin/sec- ret.py. Если пользователям будет доступен просмотр реализации этого модуля, это существенно облегчит им возможность взлома зашифрованных паролей, передаваемых через Сеть.

Чтобы уменьшить эту опасность, сценарий getfile хранит имена файлов с ограниченным доступом в списке privates и с помощью встроенной функции os.path.samefile проверяет, не совпадает ли имя запрашиваемого файла с одним из имен в списке privates. Функция samefile сравнивает идентификационную информацию обоих файлов, полученную с помощью встроенной функции os.stat (устройство и номера индексных узлов). Поэтому пути, выглядящие синтаксически различными, но ссылающиеся на один и тот же файл, считаются идентичными. Например, на сервере, использовавшемся в процессе работы над вторым изданием этой книги, следующие пути к модулю шифрования являются разными строками, но вызов os.path.samefile для них возвращает «истину»:

../PyMailCgi/secret.py

/home/crew/lutz/public_html/PyMailCgi/secret.py

К сожалению, функция os.path.samefile поддерживается в Unix, Linux и Macs, но не поддерживается в Windows. Чтобы имитировать ее действие в Windows, мы разворачиваем пути к файлам до абсолютных путей, приводим их к общему регистру символов и сравниваем (в следующем примере я сократил пути, заменив их части троеточием …, чтобы уместить по ширине страницы):

>   >> import os

>   >> os.path.samefile

AttributeError: ‘module’ object has no attribute ‘samefile’

>   >> os.getcwd()

‘C:\\\\PP4E\\dev\\Examples\\PP4E\\Internet\\Web’

>>> 

>   >> x = os.path.abspath(‘../Web/PYMailCgi/cgi-bin/secret.py’).lower()

>   >> y = os.path.abspath(‘PyMailCgi/cgi-bin/secret.py’).lower()

>>> z = os.path.abspath(‘./PYMailCGI/cgi-bin/../cgi-bin/SECRET.py’).lower()

>>> x

‘c:\\\\dev\\examples\\pp4e\\internet\\web\\pymailcgi\\cgi-bin\\secret.py’

>>> y

‘c:\\\\dev\\examples\\pp4e\\internet\\web\\pymailcgi\\cgi-bin\\secret.py’

>>> z

‘c:\\\\dev\\examples\\pp4e\\internet\\web\\pymailcgi\\cgi-bin\\secret.py’ >>>

>>> x == y, y == z

(True, True)

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

Рис. 15.31. Результат попытки доступа к закрытому файлу

 

Заметьте также, что настоящие ошибки доступа к файлам обрабатываются иначе. Проблемы нехватки прав доступа и попытки доступа к несуществующим файлам, например, перехватываются различными обработчиками исключений, которые выводят сообщения (извлекаемые
с помощью функции sys.exc_info), передавая дополнительный контекст. На рис. 15.32 показана такая страница с сообщением об ошибке.

Getfile responseWindows Internet Explorer П=П1 в IIbESiI

Favorites @ Getfile response

Source code for: ‘./PYMailCGI/cgi-bin/pymailcgi.html’


{Error opening file: [Errno 2] No such file or directory: 1./PYMailCGl/cgi-bin/pymailcgi.html1)

 

Done Qi Internet | Protected Mode: On 4^ v 125%

 

Рис. 15.32. Вывод ошибки доступа к файлу

 


 


Общее практическое правило требует подробно сообщать об исключительных ситуациях, возникших в процессе обработки файлов, особенно во время отладки сценариев. При перехвате таких исключений в сценарии программист должен позаботиться о выводе подробностей (перенаправление потока вывода sys.stderr в sys.stdout здесь не поможет, так как интерпретатор в данном случае не выводит сообщение об ошибке). Объекты с типом текущего исключения, данными и трассировочной информацией всегда доступны в модуле sys и могут быть выведены вручную.

Не устанавливайте сценарий getfile.py, если вы действительно хотите сохранить в тайне содержимое своих файлов! Проверка по списку закрытых файлов помешает непосредственно просмотреть модуль шифрования с помощью этого сценария, но он может не учитывать некоторые дополнительные возможности, особенно в Windows. Безопасность не является темой этой книги, поэтому я не стану углубляться в ее обсуждение, отмечу только, что некоторая степень паранойи в Интернете не помешает. Вы всегда должны исходить из того, что в конечном счете осуществится худший из возможных сценариев, особенно при установке систем в общедоступном Интернете.

Использованная литература:

Марк Лутц — Программирование на Python, 4-е издание, II том, 2011

Каталог сайтов Всего.ру
Оцените статью
Секреты программирования
Добавить комментарий