Building web applications ignoring security

We need a simple application. For this example, we will use CherryPy (http://cherrypy.org), a pythonic web framework that conveniently maps URLs into Python methods.

Note

If you want to know more about the CherryPy framework, I highly recommend reading CherryPy Essentials by Sylvain Hellegouarch.

After building our web application, we will review some of the options people take in securing things. Then, we will plug in Spring Python Security, showing how easy it is to lock down an application.

First, let's build a simple web application that serves wiki pages, and allows us to edit them.

import cherrypy
def forward(url):
return '<META HTTP-EQUIV="Refresh" CONTENT="0; URL=' + url + '">'
class Springwiki(object):
def __init__(self, controller = None):
self.controller = controller
@cherrypy.expose
def index(self, article="Main Page"):
page = self.controller.getPage(article)
return page.html()
@cherrypy.expose
def default(self, article="Main Page"):
return self.index(article)
@cherrypy.expose
def submit(self, article, wpTextbox=None, wpSummary=None):
self.controller.updatePage(article, wpTextbox,
wpSummary)
return forward("/" + article)
@cherrypy.expose
def edit(self, article):
page = self.controller.getEditPage(article)
return page.html()

This CherryPy application nicely maps URLs onto Python methods that are marked with the @cherrypy.expose decorator. HTTP GET/POST parameters are translated into named arguments. Method index maps to the web path /, submit maps to /submit, and edit maps to /edit. As a CherryPy application, we aren't done yet. After filling in some more details, we'll show to how to host this application inside a CherryPy web container.

In this case, our application has an injected controller which is responsible for handling updates to the wiki as well as returning components that generate the HTML content sent back to the browser.

Let's code a simple controller that processes these web requests. For now, let's use a simple Python dictionary as the place to store the information. Later on, we can migrate it to a database server.

import time
from model import EditPage
from model import NoPage
from model import Page
wiki_db = {
"Main Page":["""
Welcome to the Spring Python book's wiki!
""", [("Original", "Initial entry", "13:22, 24 November 2009")]]
}
class SpringWikiController(object):
def exists(self, article):
return article in wiki_db
def getPage(self, article):
if self.exists(article):
return Page(article=article,
wikitext=wiki_db[article][0],
controller=self)
else:
return NoPage(article=article,
controller=self)
def getEditPage(self, article):
if self.exists(article):
return EditPage(article=article,
wikitext=wiki_db[article][0],
controller=self)
else:
return EditPage(article=article,
wikitext="",
controller=self)
def create_edit_tuple(self, text, summary):
return (text,
summary,
time.strftime("%H:%M:%S %d %b %Y", time.localtime()))
def updatePage(self, article, wikitext, summary):
if self.exists(article):
wiki_db[article][1].append(
self.create_edit_tuple(wiki_db[article][0],summary))
wiki_db[article][0] = wikitext
else:
wiki_db[article] = [None, None]
wiki_db[article][1] = [
self.create_edit_tuple(wikitext,summary)]
wiki_db[article][0] = wikitext

There is a method to retrieve a page. It checks whether or not the article exists. If not, it returns a specialized page that will support creating a new one. Otherwise, it returns a normal page with the retrieved wiki text.

There is another method to return an edit page, used to edit existing article entries.

Finally, there is a function to update a current page with new wiki text. If you'll notice, when the wiki text is updated, an entry record is created in the form of a tuple, and stored in the tail end list of the article's entry.

Let's define some simple model objects to pass around our application. First, we need a basic representation of a page with some basic wiki formatting rules.

import re
intrawikiR = re.compile("[[(?P<link>.*?)(|(?P<desc>.*?))?]]")
externalLinkR = re.compile("[(?P<link>.*?)s(?P<description>.*)]")
class Page(object):
def __init__(self, article, wikitext, controller):
self.article = article
self.wikitext = wikitext
self.controller = controller
def link_substitution(self, match):
g = match.groupdict()
if self.controller.exists(g["link"]):
str = '<a href="' + g["link"] + '">'
else:
str = '<a href="edit/' + g["link"] + '">'
if g["desc"]:
str += g["desc"]
else:
str += g["link"]
str += "</a>"
return str
def header(self):
"""Standard header used for all pages"""
return """
<html>
<head>
<title>Spring Python book demo</title>
</head>
<body>
<h1>""" + self.article + """</h1>
"""
def footer(self):
"""Standard footer used for all pages."""
footer = """
<ul>
<li><a href="/edit/""" + self.article + """">Edit</a></li>
</ul>
<a href="http://springpythonbook.com">Spring Python book</a>
</body>
"""
return footer
def wiki_to_html(self):
htmlText = self.wikitext
try:
htmlText = intrawikiR.sub('<a href="g<link>">g<desc></a>', htmlText)
except:
htmlText = intrawikiR.sub('<a href="g<link>">g<link></a>', htmlText)
htmlText = externalLinkR.sub('<a href="g<link>">g<description></a>', htmlText)
return htmlText
def html(self):
results = self.header()
results += """
<!-- BEGIN main content -->
"""
results += self.wiki_to_html()
results += """
<!-- END main content -->
"""
results += self.footer()
return results

Page holds the title of the article, the wiki text associated with it, and a handle on the controller. Most of the code in this class is used to support html(), a function used to render this page in an HTML format.

Tip

Most web frameworks encourage usage of templates to avoid embedding HTML in the application. This helps decouple the information shown on the pages from the format it is displayed in. Since this chapter's focus is on security and not clean Model-View-Controller (MVC) tactics, the HTML is embedded directly into the application.

Our wiki page basically generates three parts: a header, some wiki text converted into HTML, followed by a footer(). The wiki text rules are simple for our example:

  • It supports the free linking style of Wikipedia, where [[article to link to]] maps to local web path /article_to_link_to. You can also insert a pipe followed by an alternate block of text to be displayed [[article to link|my alt text]].
  • External links [http://springpythonbook.com Spring Python book site] map to the full URL, with the text after the first space being displayed on the page

While it would be easy to implement more wiki format rules, we want to stick with demonstrating security for our web application.

Next we need the representation of an edit page.

class EditPage(Page):
def __init__(self, article, wikitext, controller):
Page.__init__(self, article, wikitext, controller)
def header(self):
"""Standard header used for all pages"""
return """
<html>
<head>
<title>Spring Python book demo</title>
</head>
<body>
<h1>Editing """ + self.article + """</h1>
"""
def wiki_to_html(self):
htmlText = """
<form method="post"
action="/submit?article=""" + 
self.article + """" enctype="multipart/form-data">
<textarea name="wpTextbox"
rows='25' cols='80'>""" + 
self.wikitext + """</textarea>
<br/>
Summary: <input type='text' value=""
name="wpSummary" maxlength='200' size='60'/>
<br/>
<input type='submit' value="Save page"
title="Save your changes"/>
<em><a href="/""" + self.article + """" title='""" + 
self.article + """'>Cancel</a></em>
</form>
"""
return htmlText

An EditPage is really a Page with some special rendering. The header() block inserts Editing in front of the article name. The main block of HTML shows a form with the wiki text, summary input box, and a Save button.

Let's create the final object modeling a non-existent page.

class NoPage(Page):
def __init__(self, article, controller):
Page.__init__(self, article,
"This page does not yet exist.", controller)

NoPage is a Page with some hard-coded wiki text on it. It inherits the same format rules as Page, and also has an edit button, so the user can replace the non-existent page with a real one.

Let's create a pure Python application context to wire these components together.

import controller
import view
from springpython.config import PythonConfig
from springpython.config import Object
class SpringWikiAppContext(PythonConfig):
def __init__(self):
super(SpringWikiAppContext, self).__init__()
@Object
def view(self):
return view.Springwiki(self.controller())
@Object
def controller(self):
return controller.SpringWikiController()

With all the parts coded up, let's write a CherryPy server application to host our web application.

import cherrypy
import os
import noxml
from springpython.context import ApplicationContext
if __name__ == '__main__':
cherrypy.config.update({'server.socket_port': 8003})
applicationContext = ApplicationContext(noxml.SpringWikiAppContext())
cherrypy.tree.mount(
applicationContext.get_object("view"),
'/',
config=None)
cherrypy.engine.start()
cherrypy.engine.block()

Assuming our controller code is in controller.py, the view code is in view.py, and the application context is in noxml.py, we should be able to fire up our CherryPy application.

Building web applications ignoring security

CherryPy runs its own web server, and in this case we have configured it to run on port 8003. It then creates an instance of our application context. Next, it grabs the view object, and mounts it with the web path /. Finally, it starts up the CherryPy engine and blocks for any web requests. Open up a browser and point at http://localhost:8003, and you will see the results.

Building web applications ignoring security

Looking at our web application from 10,000 feet

The following diagram shows a high level view of our web application as a user looks up an article, clicks on the Edit button, makes changes, and then submits an update. By clicking on the Save button, the new wiki text is posted to Spring Wiki.

Looking at our web application from 10,000 feet

In our example application, we have coded a simplistic database: a Python dictionary. However, that could easily be replaced by a relational database hosted on a separate server. The concept is the same: save the changes submitted by the user.

If we started out developing this application on the premise that anyone can read and edit any article, this solution works. It is simple, easy to understand, and probably the only security needed would be a firewall to protect our servers from external attack.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.144.56