Read Programming Python Online

Authors: Mark Lutz

Tags: #COMPUTERS / Programming Languages / Python

Programming Python (166 page)

BOOK: Programming Python
12.36Mb size Format: txt, pdf, ePub
ads
Security Protocols

In
onViewPswdSubmit
’s
source code (
Example 16-7
), notice that password
inputs are passed to an
encode
function as they are added to the parameters dictionary; this causes
them to show up encrypted or otherwise obfuscated in hyperlinked URLs.
They are also URL encoded for transmission (with
%
escapes if needed) and are later decoded and
decrypted within other scripts as needed to access the POP account. The
password encryption step,
encode
, is
at the heart of PyMailCGI’s security policy.

In Python today, the standard library’s
ssl
module
supports Secure Sockets Layer (SSL)
with its socket wrapper call, if the required library is
built into your Python. SSL automatically encrypts transmitted data to
make it safe to pass over the Net. Unfortunately, for reasons we’ll
discuss when we reach the
secret.py
module later in this chapter (see
Example 16-13
), this wasn’t a
universal solution for PyMailCGI’s password data. In short, the
Python-coded web server we’re using doesn’t directly support its end of
a secure HTTP encrypted dialog, HTTPS. Because of that, an alternative
scheme was devised to minimize the chance that email account information
could be stolen off the Net in transit.

Here’s how it works. When this script is invoked by the password
input page’s form, it gets only one input parameter: the password typed
into the form. The username is imported from a
mailconfig
module installed on the server; it
is not transmitted together with the unencrypted password because such a
combination could be harmful if intercepted.

To pass the POP username and password to the next page as state
information, this script adds them to the end of the mail selection list
URLs, but only after the password has been encrypted or obfuscated by
secret.encode
—a function in a module
that lives on the server and may vary in every location that PyMailCGI
is installed. In fact,
PyMailCGI
was written to not have to know about the password encryptor at all;
because the encoder is a separate module, you can provide any flavor you
like. Unless you also publish your encoder module, the encoded password
shipped with the username won’t mean much if seen.

The upshot is that normally PyMailCGI never sends or receives both
username and password values together in a single transaction, unless
the password is encrypted or obfuscated with an encryptor of your
choice. This limits its utility somewhat (since only a single account
username can be installed on the server), but the alternative of popping
up two pages—one for password entry and one for username—seems even less
friendly. In general, if you want to read your mail with the system as
coded, you have to install its files on your server, edit its
mailconfig.py
to reflect your account
details, and change its
secret.py
encoder and decoder as desired.

Reading mail with direct URLs

One exception: since
any CGI script can be invoked with parameters in an
explicit URL instead of form field values, and since
commonhtml
tries to fetch inputs from the
form object before importing them from
mailconfig
, it is possible for any person to
use this script if installed at an accessible address to check his or
her mail without installing and configuring a copy of PyMailCGI of
their own. For example, a URL such as the following typed into your
browser’s address field or submitted with tools such as
urllib.request
(but without the line break
used to make it fit here):

http://localhost:8000/cgi-bin/
onViewPswdSubmit.py?user=lutz&pswd=guess&site=pop.earthlink.net

will actually load email into a selection list page such as that
in
Figure 16-8
, using
whatever user, password, and mail site names are appended to the URL.
From the selection list, you may then view, reply, forward, and delete
email.

Notice that at this point in the interaction, the password you
send in a URL of this form is
not
encrypted.
Later scripts expect that the password inputs will be sent encrypted,
though, which makes it more difficult to use them with explicit URLs
(you would need to match the encrypted or obfuscated form produced by
the
secret
module on the server).
Passwords are encoded as they are added to links in the reply page’s
selection list, and they remain encoded in URLs and hidden form fields
thereafter.

Warning

But you shouldn’t use a URL like this, unless you don’t care
about exposing your email password. Sending your unencrypted mail
user ID and password strings across the Net in a URL such as this is
unsafe and open to interception. In fact, it’s like giving away your
email—anyone who intercepts this URL or views it in a server logfile
will have complete access to your email account. It is made even
more treacherous by the fact that this URL format appears in a book
that will be distributed all around the world.

If you care about security and want to use PyMailCGI on a
remote server, install it on your server and configure
mailconfig
and
secret
. That should at least guarantee
that both your user and password information will never be
transmitted unencrypted in a single transaction. This scheme still
may not be foolproof, so be careful out there. Without secure HTTP
and sockets, the Internet is a “use at your own risk” medium.

The Message View Page

Back to our
page flow; at this point, we are still viewing the message
selection list in
Figure 16-8
. When we click on
one of its generated hyperlinks, the stateful URL invokes the script in
Example 16-8
on the server,
sending the selected message number and mail account information (user,
password, and site) as parameters on the end of the script’s URL.

Example 16-8. PP4E\Internet\Web\PyMailCgi\cgi-bin\onViewListLink.py

#!/usr/bin/python
"""
################################################################################
On user click of message link in main selection list: make mail view page;
cgi.FieldStorage undoes any urllib.parse escapes in link's input parameters
(%xx and '+' for spaces already undone); in 2.0+ we only fetch 1 mail here, not
the entire list again; in 2.0+ we also find mail's main text part intelligently
instead of blindly displaying full text (with any attachments), and we generate
links to attachment files saved on the server; saved attachment files only work
for 1 user and 1 message; most 2.0 enhancements inherited from mailtools pkg;
3.0: mailtools decodes the message's full-text bytes prior to email parsing;
3.0: for display, mailtools decodes main text, commonhtml decodes message hdrs;
################################################################################
"""
import cgi
import commonhtml, secret
from externs import mailtools
#commonhtml.dumpstatepage(0)
def saveAttachments(message, parser, savedir='partsdownload'):
"""
save fetched email's parts to files on
server to be viewed in user's web browser
"""
import os
if not os.path.exists(savedir): # in CGI script's cwd on server
os.mkdir(savedir) # will open per your browser
for filename in os.listdir(savedir): # clean up last message: temp!
dirpath = os.path.join(savedir, filename)
os.remove(dirpath)
typesAndNames = parser.saveParts(savedir, message)
filenames = [fname for (ctype, fname) in typesAndNames]
for filename in filenames:
os.chmod(filename, 0o666) # some srvrs may need read/write
return filenames
form = cgi.FieldStorage()
user, pswd, site = commonhtml.getstandardpopfields(form)
pswd = secret.decode(pswd)
try:
msgnum = form['mnum'].value # from URL link
parser = mailtools.MailParser()
fetcher = mailtools.SilentMailFetcher(site, user, pswd)
fulltext = fetcher.downloadMessage(int(msgnum)) # don't eval!
message = parser.parseMessage(fulltext) # email pkg Message
parts = saveAttachments(message, parser) # for URL links
mtype, content = parser.findMainText(message) # first txt part
commonhtml.viewpage(msgnum, message, content, form, parts) # encoded pswd
except:
commonhtml.errorpage('Error loading message')

Again, much of the work here happens in the
commonhtml
module, listed later in this
section (see
Example 16-14
).
This script adds logic to decode the input password (using the
configurable
secret
encryption
module) and extract the selected mail’s headers and text using
the
mailtools
module
package from
Chapter 13
again. The full
text of the selected message is ultimately fetched, parsed, and decoded
by
mailtools
, using the standard
library’s
poplib
module and
email
package. Although we’ll have to
refetch this message if viewed again, version 2.0 and later do not grab
all mails to get just the one selected.
[
68
]

Also new in version 2.0, the
saveAttachments
function in this script splits
off the parts of a fetched message and stores them in a directory on the
web server machine. This was discussed earlier in this chapter—the view
page is then augmented with URL links that point at the saved part
files. Your web browser will open them according to their filenames and
content. All the work of part extraction, decoding, and naming is
inherited
from
mailtools
. Part files are kept temporarily;
they are deleted when the next message is fetched. They are also
currently stored in a single directory and so apply to only a single
user.

If the message can be loaded and parsed successfully, the result
page, shown in
Figure 16-12
, allows us to
view, but not edit, the mail’s text. The function
commonhtml.viewpage
generates a “read-only”
HTML option for all the text widgets in this page. If you look closely,
you’ll notice that this is the mail we sent to ourselves in
Figure 16-3
and which showed
up at the end of the list in
Figure 16-8
.

Figure 16-12. PyMailCGI view page

View pages like this have a pull-down action selection list near
the bottom; if you want to do more, use this list to pick an action
(Reply, Forward, or Delete) and click on the Next button to proceed to
the next screen. If you’re just in a browsing frame of mind, click the
“Back to root page” link at the bottom to return to the main page, or
use your browser’s Back button to return to the selection list
page.

As mentioned,
Figure 16-12
displays the
mail we sent earlier in this chapter, being viewed after being fetched.
Notice its “Parts:” links—when clicked, they trigger URLs that open the
temporary part files on the server, according to your browser’s rules
for the file type. For instance, clicking on the “.txt” file will likely
open it in either the browser or a text editor. In other mails, clicking
on “.jpg” files may open an image viewer, “.pdf” may open Adobe Reader,
and so on.
Figure 16-13
shows the
result of clicking the “.py” attachment part of
Figure 16-12
’s
message in Chrome.

Figure 16-13. Attached part file link display

Passing State Information in HTML Hidden Input Fields

What you don’t see on
the view page in
Figure 16-12
is
just as important as what you do see. We need to defer to
Example 16-14
for coding details, but
something new is going on here. The original message number, as well as
the POP user and (still encoded) password information sent to this
script as part of the stateful link’s URL, wind up being copied into the
HTML used to create this view page, as the values of hidden input fields
in the form. The hidden field generation code in
commonhtml
looks like this:

print('
' % urlroot)
print('' % msgnum)
print('' % user) # from page|url
print('' % site) # for deletes
print('' % pswd) # pswd encoded

As we’ve learned, much like parameters in generated hyperlink
URLs, hidden fields in a page’s HTML allow us to embed state information
inside this web page itself. Unless you view that page’s source, you
can’t see this state information because hidden fields are never
displayed. But when this form’s Submit button is clicked, hidden field
values are automatically transmitted to the next script along with the
visible fields on the form.

Figure 16-14
shows
part of the source code generated for another message’s view page; the
hidden input fields used to pass selected mail state information are
embedded near the top.

Figure 16-14. PyMailCGI view page, generated HTML

The net effect is that hidden input fields in HTML, just like
parameters at the end of generated URLs, act like temporary storage
areas and retain state between pages and user interaction steps. Both
are the Web’s simplest equivalent to programming
language
variables. They come in handy
anytime your application needs to remember something between
pages.

Hidden fields are especially useful if you cannot invoke the next
script from a generated URL hyperlink with parameters. For instance, the
next action in our script is a form submit button (Next), not a
hyperlink, so hidden fields are used to pass state. As before, without
these hidden fields, users would need to reenter POP account details
somewhere on the view page if they were needed by the next script (in
our example, they are required if the next action is Delete).

BOOK: Programming Python
12.36Mb size Format: txt, pdf, ePub
ads

Other books

Born at Midnight by C. C. Hunter
Sierra's Homecoming by Linda Lael Miller
Dragon's Winter by Elizabeth A. Lynn
The Ministry of Pain by Dubravka Ugresic
Come Endless Darkness by Gary Gygax
Girl Watcher's Funeral by Hugh Pentecost