summaryrefslogtreecommitdiffstats
path: root/accounts/utils
diff options
context:
space:
mode:
authorAlexander Sulfrian <alexander@sulfrian.net>2016-01-24 03:55:49 +0100
committerAlexander Sulfrian <alexander@sulfrian.net>2016-02-02 04:22:16 +0100
commit5e7e7fc832d26178a6036ed483fe3cfffe2b22b2 (patch)
treec74302270b7c262d744876f1d2f93bc84c44a2ba /accounts/utils
parent6eb1db6bff15e1611767f5219ee1b4ea558e3d28 (diff)
downloadweb-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__.py29
-rw-r--r--accounts/utils/sessions.py62
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()