I recently had my first shot at writing cgi scripts in python. It was an altogether different experience having come from a .Net and Turbogears background. I also got my hands on a Linux server. Infact, it was the first time I got to use vi, ls, cat, cp etc, since college. It is really amazing how much you can do with just the shell commands on Linux.
The Linux machine had an apache server which ran some cgi scripts to do some basic tasks like creating a new user record in the database, deleting, editing etc. I think that was the reason they did not go beyond cgi. These tasks were manually done by an admin after logging into the application. Now, these scripts were required to be made callable from an asp.net web application.
ASP.NET application <===========> Python cgi scripts
CreateDemo.aspx _____________ create_demo.cgi
(collects user info
for demo record) _____________ (requires authentication)
So, we decided to create another script, let us say 'authenticate.cgi', in which we used urllib python library to by pass the authentication on create_demo.cgi and then CreateDemo.aspx would call authenticate.cgi. Here is the new flow-
CreateDemo.aspx --> authenticate.cgi --> create_demo.cgi
This write-up addresses two key areas:
1. How to communicate with a python cgi script from an asp.net web page?
//Request url and request method
HttpWebRequest httpRequest = HttpWebRequest)WebRequest.Create("http://192.1.1.1/no_auth/authenticate.cgi?name=harpreet”);
httpRequest.KeepAlive = false;
httpRequest.Method = "GET";
//Read the web response from the URI
string strResponse = string.Empty;
using (WebResponse webResponse = (WebResponse)httpRequest.GetResponse())
{
System.Text.Encoding enc = System.Text.Encoding.GetEncoding(1252);
using (StreamReader srResponseStream = new StreamReader(webResponse.GetResponseStream(), enc))
{
//Read the response into a string or you could use XML also
strResponse = srResponseStream.ReadToEnd();
}
}
2. How to by pass basic authentication on cgi script using urllib?
#!/usr/bin/env /usr/local/bin/python
import urllib2,urllib
import sys,os,cgi,cgitb
import re,datetime
import base64
from urlparse import urlparse
def handler():
form = cgi.FieldStorage()
if form.list:
data = {}
for field in flds:
data[field]=form.getvalue(field,'')
theurl = 'http://localhost/admin/create_demo.cgi’
# you'll need to supply a protected page with your username and password
username = 'scott'
password = 'tiger' # a very bad password
req = urllib2.Request(theurl)
try:
handle = urllib2.urlopen(req, urllib.urlencode(data))
except IOError, e:
# do whatever you want to handle the exception here, 'pass' in this example
pass
else:
# If we don't fail then the page isn't protected
print "This page is not protected by any authentication."
sys.exit(1)
if not hasattr(e, 'code') or e.code != 401:
# we got an error - but not a 401 error
print "This page is not protected by any authentication."
print 'Some other reason for failure'
sys.exit(1)
authheader = e.headers['www-authenticate']
# this gets the www-authenticate line from the headers
# which would contain the authentication scheme and realm
authobj = re.compile(r'''(?:\s*www-authenticate\s*:)?\s*(\w*)\s+realm=['"]([^'"]+)['"]''',re.IGNORECASE)
# this RE is used to extract scheme and realm
matchobj = authobj.match(authheader)
# if the authheader isn't matched by the regular expression
# then there is probably some error in the header
if not matchobj:
print 'The authentication header is badly formed.'
sys.exit(1)
scheme = matchobj.group(1)
realm = matchobj.group(2)
# check schem (this example works for basic authorization only
if scheme.lower() != 'basic':
print 'This example only works for BASIC Authorization.'
sys.exit(1)
base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
authheader = "Basic %s" % base64string
req.add_header("Authorization", authheader)
try:
handle = urllib2.urlopen(req,urllib.urlencode(data))
except IOError, e:
print "The username or password is not valid."
sys.exit(1)
thepage = handle.read()
print "Content-type: text/html; charset=utf-8"
print thepage
handler()
Security was not really a major concern for this cgi, but an IP based security was added to limit unwanted access.
* The code snippets are not really tested but, will hopefully work :-)