Login

Saving passwords for other services (semi-)securely in a database

Author:
equanimity
Posted:
February 19, 2009
Language:
Python
Version:
1.0
Tags:
password crypto
Score:
2 (after 2 ratings)

I've often found myself wanting to store passwords for other web services (e.g. maillist managers, IMAP accounts, IM accounts etc) for use by a web application, but have not wanted to hard-code them in settings.py or store them as plaintext in the database.

This uses the pycrypto library to encrypt each bit of information using the concatenation of a salt value and the SECRET_KEY value from your settings.py, hopefully leading to a bit more security and flexibility.

You'll probably want to add some views to let people edit these things as I can't find a way to make the admin interface play nicely with it.

Example usage:

In [1]: p = Password(
            name='IMAP account', slug='imap',
            username='example', password='password',
            host='imap.gmail.com'
        )

In [2]: p.host
Out[2]: 'imap.gmail.com'

In [3]: p.e_host
Out[3]: '6wdyMDKYy8c=$YXw6t/Q9wI[...]'

In [4]: p.save()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
from os import urandom
from base64 import b64encode, b64decode
from django.db import models
from Crypto.Cipher import ARC4


def get_value(name):
    def f(self):
        return Password.decrypt(getattr(self, 'e_%s'%name))
    return f
    
def set_value(name):
    def f(self, value):
        setattr(self, 'e_%s'%name, Password.encrypt(value))
    return f
    

class Password(models.Model):
    SALT_SIZE = 8
    
    name = models.CharField(max_length=128)
    slug = models.SlugField()
    e_username = models.TextField(blank=True)
    e_password = models.TextField(blank=True)
    e_host = models.TextField(blank=True)
    e_resource = models.TextField(blank=True)

    @staticmethod
    def encrypt(plaintext):
        salt = urandom(Password.SALT_SIZE)
        arc4 = ARC4.new(salt + settings.SECRET_KEY)
        plaintext = "%3d%s%s" % (len(plaintext), plaintext, urandom(256-len(plaintext)))
        return "%s$%s" % (b64encode(salt), b64encode(arc4.encrypt(plaintext)))
        
    @staticmethod
    def decrypt(ciphertext):
        salt, ciphertext = map(b64decode, ciphertext.split('$'))
        arc4 = ARC4.new(salt + settings.SECRET_KEY)
        plaintext = arc4.decrypt(ciphertext)
        return plaintext[3:3+int(plaintext[:3].strip())]
    
    def encrypted_property(name):
        return property(get_value(name), set_value(name))    
    

    username = encrypted_property('username')
    password = encrypted_property('password')
    host = encrypted_property('host')
    resource = encrypted_property('resource')

More like this

  1. Basic Auth Middleware by joshsharp 3 years, 8 months ago
  2. JSONField by deadwisdom 7 years, 6 months ago
  3. ByteSplitterField by Lacour 3 years, 6 months ago
  4. PositionField by jpwatts 6 years, 7 months ago
  5. Obfuscator for django project sources by audial 7 years, 3 months ago

Comments

Please login first before commenting.