Login

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

Author:
equanimity
Posted:
February 19, 2009
Language:
Python
Version:
1.0
Score:
4 (after 4 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. Template tag - list punctuation for a list of items by shapiromatron 3 months ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 3 months, 1 week ago
  3. Serializer factory with Django Rest Framework by julio 10 months, 1 week ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 10 months, 4 weeks ago
  5. Help text hyperlinks by sa2812 11 months, 3 weeks ago

Comments

limbail (on August 21, 2015):

You can adapt it to admin with something like this:

In admin.py file: class MyClass(BLA,BLA): resource_class = LockerModelResource

def save_model(self, request, obj, form, change):
    req_user = request.GET.get('', str(obj.e_username))
    obj.username = req_user
    obj.save()

With this, you can now add a encrypted password in admin.

#

Please login first before commenting.