13 Dec 2007

Never store passwords as clear text

Never store passwords as clear text, that is the general rule with any application that has a database backend that is used for authentication into the system. Why?

Basic authentication with a database usually works by comparing username and password combination that the user entered to values in the database table containing user details such as login name, password etc…It might be possible for a user with correct credentials to be able to inject SQL queries to the application, something like;


assuming the user can guess or knows the table containing user data.

If passwords are in clear text then lo and behold the users now has access to all other users login name and passwords. Anyway this post is not about security of database backed applications but a post about how i overcame different versions of Python and py-bcrypt’s support.

As i’ve posted before i’ve been working at a web development company as a Python programmer. It’s a Zope ‘shop’ in the sense that there main application, developed in house a shopping cart system is developed using Zope. Anyway i just developed ‘External Scripts’ to do specialized stuff for customers. But recently i’ve been working on a ‘time/job tracking’ web application using web.py. I’ve mentioned this in a previous post. So here is the versions of Python,web.py and py-bcrypt that i used to develop the tracker;

  • Python 2.5.1

  • py-bcrypt-0.1 (Python 2.4 or higher)

  • MySQL-5

Anyway, this past week the application was put on a live production server. That has;

  • Python2.3

  • Postgres

I’ve also used decorators to ‘decorate’ functions to restrict access to certain URL paths. Take a guess in which version decorators was introduced into Python? You guessed right Python 2.4 got blessed with decorators. Guess what the lead/senior developer did ? He re-wrote most of the code to just use plain function calls instead of decorators…he re-wrote…instead of the simpler solution of installing Python >=2.4. (Converted the decorator to a plain function which is called in all other functions that was decorated with it).

I mentioned above that py-bcrypt requires Python >= 2.4 because it needs the function os.urandom which was introduced into Python 2.4. Oh slap! i’ve got Python 2.3 so we can’t generate hashes…the solution that was suggested to me was….“write/copy urandom” i think that was one of the moments in my not so long professional career that i thought that someone more senior and with more was wrong. I would have instead installed Python 2.5 on the server, which seemed to be the ‘path of least resistance’.

It seemed a daunting task. The first step i took was looking at py-bcrypt module. It contains two files;

  • init.py

  • _bcrytp.so

You can’t edit _bcrytp.so file as it a library file. So i looked at init.py, which imports the os module and defines the gensalt function. From my the Python 2.5 installation on my Mac i copied the urandom implementation into init.py just above the line where import os . urandom is not that complex , it just tries to open /dev/urandom and reads in n number of bytes and return it. So here is what init.py looks like after the changes, its a hack.

25 from os import O_RDONLY,read
27 def urandom(n):
28     """urandom(n) -> str
30     Return a string of n random bytes suitable for cryptographic use.
32     """
33”     try:
34         _urandomfd = open("/dev/urandom",r )
35     except (OSError, IOError):
36         raise Exception("/dev/urandom (or equivalent) not found")
37     bytes = ""
39     while len(bytes) < n:
40         bytes += _urandomfd.read(n-len(bytes))
41         #bytes += read(_urandomfd, n - len(bytes))
43     _urandomfd.close()

44     return bytes

46 import os

47 os.urandom = urandom
48 from _bcrypt import 

Now py-bcrypt works and passwords are hashable.

The thing that troubles me and is a question on my mind, is it worth the risk to install Python >= 2.4 on a server that contains ‘live shops’ ? The risk being totally blowing up the default Python installation (2.3) and bringing down the shops ? I would have probably installed a new version of Python and sandboxed it. The irony is that the senior developer was the one who chose py-crypt and told me to come up with a decorator for methods which need to be password protected. I would have thought that with his experience he would have guessed that the request for the app to go online would have come. Also if you are scared to blow the default installation of Python on the production server, WHY PUT AN IN HOUSE APPLICATION THERE?