Выше мы вкратце обсуждали подход к защите паролей, принятый в Py- MailCGI. Здесь мы рассмотрим его конкретную реализацию. Программа PyMailCGI передает имя пользователя и пароль между страницами с помощью скрытых полей форм и параметров запроса в адресах URL, встраиваемых в разметку HTML страниц. Мы рассмотрели эти приемы в предыдущей главе. Такие данные передаются через сетевые сокеты в виде простого текста — внутри разметки HTML ответа сервера — и в виде параметров в запросах, посылаемых клиентом. При таком подходе возникает проблема защиты секретных данных.
Эта особенность не является проблемой при использовании локального веб-сервера, как и во всех примерах до сих пор. Данные в этом случае передаются между двумя программами, выполняющимися на одном и том же компьютере, и недоступны внешнему миру. Однако если вам потребуется установить PyMailCGI на удаленный веб-сервер, это может стать проблемой. Поскольку эти данные желательно хранить втайне от посторонних, в идеале хотелось бы иметь способ скрывать их при передаче и предотвращать возможность подсмотреть их в файлах журналов на сервере. С появлением новых и уходом старых возможностей приемы решения этой проблемы неоднократно изменялись на протяжении жизни этой книги:
• Во втором издании этой книги был разработан собственный модуль шифрования, использующий модуль шифрования rotor из стандартной библиотеки. Этот модуль использовался для шифрования данных, вставляемых в поток ответа сервера, и затем для расшифровывания данных, возвращаемых клиентом в параметрах. К сожалению, модуль rotor был исключен из стандартной библиотеки в версии Python 2.4 из-за проблем, связанных с безопасностью. Возможно, это было слишком радикальное решение (модуль rotor вполне пригоден для использования в простых приложениях), тем не менее, в последних версиях Python модуль rotor более недоступен.
• В третьем издании модель второго издания была расширена за счет добавления поддержки шифрования паролей с помощью сторонних модулей и открытой системы PyCrypto. К сожалению, эта система доступна только для Python 2.X и к моменту написания этих строк для четвертого издания в середине 2010 года версия для 3.X еще не вышла (хотя некоторый прогресс в этом направлении имеется). Кроме того, классы Python с реализацией веб-сервера, выполняемого локально и используемого в этом издании для опробования примеров, в Python 3.1 все еще не поддерживают защищенный протокол HTTPS — законченное решение, обеспечивающее безопасность в Веб, о котором я расскажу чуть ниже.
• Вследствие всего вышеперечисленного в этом четвертом издании сохранена унаследованная поддержка модуля rotor и системы PyCryp- to, если они будут установлены, а на крайний случай реализовано упрощенное шифрование пароля, алгоритм которого можно изменять для каждой установки PyMailCGI. Поскольку эту версию в целом можно считать лишь прототипом, дальнейшее улучшение этой модели, включая поддержку HTTPS при выполнении под управлением более надежных веб-серверов, я оставляю в качестве самостоятельного упражнения.
Шифрование данных вручную: rotor
(более не существующий)
В принципе, сценарии CGI могут вручную шифровать любые данные, добавляемые в поток ответа, как это было реализовано в версии PyMailCGI для второго издания этой книги. Однако с исключением модуля rotor из версии Python 2.4 в стандартной библиотеке не осталось инструментов шифрования для решения этой задачи. Кроме того, использование программного кода из оригинального модуля rotor нежелательно, с точки зрения сопровождения; к тому же задействовать его не так просто, потому что он был написан на языке C (недостаточно будет просто скопировать файл .py из более ранней версии Python). Если только вы не используете старую версию Python, модуль rotor практически недоступен.
Главным образом из исторического интереса и для сравнения с современными приемами ниже демонстрируется, как использовался этот модуль. Он был основан на алгоритме шифрования Enigma: создавался новый объект rotor с ключом (и, при необходимости, со счетчиком циклов) и вызывались его методы encrypt и decrypt:
> >> import rotor
> >> r = rotor.newrotor(‘pymailcgi’) # (ключ, [,счетчик])
> >> r.encrypt(‘abc123′) # может возвращать непечатаемые символы
‘ \323an\021\224′
> >> x = r.encrypt(‘spam123′) # результат имеет ту же длину, что
> >> x # и исходная строка
‘* _\344\011pY’
> >> len(x)
7
> >> r.decrypt(x)
‘spam123’
Обратите внимание, что один и тот же объект rotor может зашифровывать несколько строк, результат может содержать непечатаемые символы (выводимые как экранированные последовательности \ascii) и длина результата всегда совпадает с длиной исходной строки. Самое главное, что строка, зашифрованная с помощью объекта rotor, может быть расшифрована в другом процессе (например, позднее другим сценарием CGI), если объект rotor создать заново:
> >> import rotor
> >> r = rotor.newrotor(‘pymailcgi‘) # может быть расшифрована в др. процессе
> >> r.decrypt(‘* _\344\011pY‘) # 2 символа представлены экранированными
‘spam123′ # последовательностями "\ascii"
> >> from secret import encode, decode
> >> x = encode(‘abc$#<>&+’) # это делают сценарии CGI
>>> x
‘ \323a\016\317\326\023\0163′
> >> import urllib.parse # это делает urlencode
> >> y = urllib.parse.quote_plus(x)
> >> y
‘+%d3a%0e%cf%d6%13%0e3’
> >> a = urllib.parse.unquote_plus(y) # это делает cgi.FieldStorage
>>> a
‘ \323a\016\317\326\023\0163′
> >> decode(a) # это делают сценарии CGI
‘abc$#<>&+’
Несмотря на то, что в настоящее время модуль rotor используется уже не так широко, те же приемы можно использовать для реализации других алгоритмов шифрования.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011