diff options
author | Alexander Sulfrian <alexander@sulfrian.net> | 2016-01-24 03:55:49 +0100 |
---|---|---|
committer | Alexander Sulfrian <alexander@sulfrian.net> | 2016-02-02 04:22:16 +0100 |
commit | 5e7e7fc832d26178a6036ed483fe3cfffe2b22b2 (patch) | |
tree | c74302270b7c262d744876f1d2f93bc84c44a2ba /accounts/utils | |
parent | 6eb1db6bff15e1611767f5219ee1b4ea558e3d28 (diff) | |
download | web-5e7e7fc832d26178a6036ed483fe3cfffe2b22b2.tar.gz web-5e7e7fc832d26178a6036ed483fe3cfffe2b22b2.tar.bz2 web-5e7e7fc832d26178a6036ed483fe3cfffe2b22b2.zip |
Encrypt the session data by default
Before we just encrypted the password, now we encrypt the whole session
information by default.
Diffstat (limited to 'accounts/utils')
-rw-r--r-- | accounts/utils/__init__.py | 29 | ||||
-rw-r--r-- | accounts/utils/sessions.py | 62 |
2 files changed, 63 insertions, 28 deletions
diff --git a/accounts/utils/__init__.py b/accounts/utils/__init__.py index 8f68733..1538fd6 100644 --- a/accounts/utils/__init__.py +++ b/accounts/utils/__init__.py @@ -6,13 +6,11 @@ import pickle import re import struct from base64 import urlsafe_b64encode, urlsafe_b64decode -from Crypto.Cipher import AES from functools import wraps from flask import current_app, flash, g, redirect, render_template, request, session from flask import url_for as flask_url_for from hashlib import sha1 from itertools import izip -from random import randint from time import time from werkzeug.exceptions import Forbidden from wtforms.validators import Regexp, ValidationError @@ -78,7 +76,7 @@ def login_user(username, password): return False session['username'] = username - session['password'] = encrypt_password(password) + session['password'] = password return True @@ -89,31 +87,6 @@ def logout_user(): g.user = None -def pad(s, numbytes=32, padding='\0'): - return s + (numbytes - len(s) % numbytes) * padding - -def encrypt_password(password): - """ - Encrypt the given password with `config.PASSWORD_ENCRYPTION_KEY`. - The key must be 32 bytes long. - """ - assert len(current_app.config['PASSWORD_ENCRYPTION_KEY']) == 32 - - password = ensure_utf8(password) - - iv = ''.join(chr(randint(0, 0xff)) for i in range(16)) - encryptor = AES.new(current_app.config['PASSWORD_ENCRYPTION_KEY'], AES.MODE_CBC, iv) - return iv + encryptor.encrypt(pad(password)) - -def decrypt_password(ciphertext): - """ - Decrypt the given password with `config.PASSWORD_ENCRYPTION_KEY`. - """ - iv = ciphertext[:16] - encryptor = AES.new(current_app.config['PASSWORD_ENCRYPTION_KEY'], AES.MODE_CBC, iv) - return encryptor.decrypt(ciphertext[16:]).rstrip('\0').decode('utf8') - - def make_confirmation(realm, data): """ Create a confirmation token e.g. for confirmation mails. diff --git a/accounts/utils/sessions.py b/accounts/utils/sessions.py new file mode 100644 index 0000000..cd12030 --- /dev/null +++ b/accounts/utils/sessions.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +from Crypto import Random +from Crypto.Cipher import AES +from flask import current_app +from flask.sessions import TaggedJSONSerializer, SecureCookieSessionInterface +from itsdangerous import BadPayload + + +def _pad(value, block_size): + padding = block_size - len(value) % block_size + return value + (padding * chr(padding)) + + +def _unpad(value): + pad_length = ord(value[len(value)-1:]) + return value[:-pad_length] + + +class EncryptedSerializer(TaggedJSONSerializer): + + def __init__(self): + self.block_size = AES.block_size + + def _cipher(self, iv): + return AES.new( + current_app.config['SESSION_ENCRYPTION_KEY'], + AES.MODE_CBC, iv) + + def dumps(self, value): + """ + Encrypt the serialized values with `config.SESSION_ENCRYPTION_KEY`. + The key must be 32 bytes long. + """ + assert len(current_app.config['SESSION_ENCRYPTION_KEY']) == 32 + + serialized_value = super(EncryptedSerializer, self).dumps(value) + + raw = _pad(serialized_value, self.block_size) + iv = Random.new().read(self.block_size) + return iv + self._cipher(iv).encrypt(raw) + + def loads(self, value): + """ + Decrypt the given serialized session data with + `config.SESSION_ENCRYPTION_KEY`. + """ + iv = value[:self.block_size] + raw = self._cipher(iv).decrypt(value[AES.block_size:]) + return super(EncryptedSerializer, self).loads(_unpad(raw)) + + +class EncryptedSessionInterface(SecureCookieSessionInterface): + serializer = EncryptedSerializer() + + def open_session(self, *args, **kwargs): + try: + parent = super(EncryptedSessionInterface, self) + return parent.open_session(*args, **kwargs) + except BadPayload: + return self.session_class() |