Hey you!

Postcards are cool! Go send some ->

postme.me

Small trick for seamless base64 password storage in django

OAuth logo
Image via Wikipedia

These days even the noobiest of the noobs know that passwords should never be stored in plain-tect on the server. For various good and bad reasons, but the gist of it is security through obscurity.

What a few less people know is that base64 is the same as plaintext. Not only is it a very simple two way hashing function, if it can be called that, it’s certainly not encryption, what’s worse is that any coder worth their salt can recognise base64 encoding at a glance. Decoding it is trivial, many tools online can do it.

On top of all of that, django already stores its user’s passwords very securily with one-way hashing AND salting.

So what the fuck am I doing trying to save passwords in base64?

Problem

The reason is in fact quite simple: External API‘s.

Here’s the problem: what do you do when your service is accessing a third party API, which doesn’t support OAuth or OAuth is impractical in your situation for various reasons and the API doesn’t support some other means of logging in with a hashed password. Basically, the only way you can access this API is if you know the user’s password?

The obvious solution is to fuck it and store plain-text passwords. Nothing you can do right? Hopefully nobody will hack your database and get to the passwords.

Sure. But what when you’re browsing around for debugging purposes? How do you prevent yourself from accidentally reading someone’s password?

Solution

That’s where base64 comes in.

But since we don’t want to make our code silly, here’s a simple way of achieving this without ever having to worry about it again.

class PasswordManager(models.Manager):
    use_for_related_fields = True
 
    def create(self, *args, **kwargs):
        try:
            kwargs['password'] = base64.encodestring(kwargs['password'])
        except KeyError:
            pass
 
        return super(PasswordManager, self).create(*args, **kwargs)
 
    def get(self, *args, **kwargs):
        data = super(PasswordManager, self).get(*args, **kwargs)
        try:
            data.password = base64.decodestring(data.password)
        except AttributeError:
            pass
        return data
 
class GoogleAccount(models.Model):
    user = models.ForeignKey(User, unique=True)
    email = models.CharField(max_length=255)
    password = models.CharField(max_length=255)
 
    objects = PasswordManager()

Basically we write a module manager that encodes all passwords in insert queries to base64 and decodes them again on select queries. For completeness sake it’s also good to define a filter function where we perform the decoding on the whole set of returned entries. But for this sort of thing that’s rarely needed because you’re rarely going to be performing third party actions for miriads of users at once.

This way we achieve perfectly seamless base64 storage of passwords. Nice and easy.

Enhanced by Zemanta

---
Need a freelance developer? Email me!

You should follow me on twitter
 Subscribe to RSS

4 responses so far

  • http://markos.gaivo.net/blog/ Marko

    There is no good reason to store passwords in clear. Ever. Reason for this is certainly not security through obscurity, because you can’t reverse a one-way hash function and a good one will make it really freaking difficult for you to guess the input.

    I can understand, although not condone, one time access to import data from other service, but for that you don’t need to store passwords either.

    Thanks for heads up about security of preona service :P

  • http://swizec.com Swizec

    Some features just cannot be made with one-time access to third party API’s ;)

  • http://twitter.com/zidarsk8 Miha Zidar

    Dude, seriously?

    What about all those reversible encryption methods, that are so much better than “base64″, which shouldn’t even be called an encryption method. All I see here is that you might need the plain password some time later (witch you shouldn’t even have), and encrypting that with base64 is a really really lame excuse for security.

    There are many way better ways of doing things.

    You should encode the data (in your case a password) with your own private password, and use that private password to decode the data. That way, if someone was able to get your database with all the passwords, those would still be useless, unless they got your private password, used to encode those. With base64 they would not need anything else from you, just plain data.

  • http://swizec.com Swizec

    This isn’t really about security per se, and it doesn’t really matter which reversible hashing or encryption you use, the point is the trick that makes its use seamless in django.

« Barefoot running Django protip #1: A better App... »