summaryrefslogtreecommitdiffstats
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
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.
-rw-r--r--accounts/__init__.py4
-rw-r--r--accounts/default_settings.py2
-rw-r--r--accounts/forms.py3
-rw-r--r--accounts/utils/__init__.py29
-rw-r--r--accounts/utils/sessions.py62
-rw-r--r--accounts/views/default/__init__.py4
-rw-r--r--requirements.txt1
7 files changed, 71 insertions, 34 deletions
diff --git a/accounts/__init__.py b/accounts/__init__.py
index afa348b..d8abf32 100644
--- a/accounts/__init__.py
+++ b/accounts/__init__.py
@@ -6,6 +6,7 @@ import os
from flask import Flask, g, session
from utils import *
+from utils.sessions import EncryptedSessionInterface
from views import default, admin
@@ -15,6 +16,7 @@ app.register_blueprint(admin.bp, url_prefix='/admin')
app.config.from_object('accounts.default_settings')
if 'SPLINE_ACCOUNT_WEB_SETTINGS' in os.environ:
app.config.from_envvar('SPLINE_ACCOUNT_WEB_SETTINGS')
+app.session_interface = EncryptedSessionInterface()
app.all_services = account.SERVICES #TODO: take that from our json file or so
app.user_backend = get_backend(app.config['USER_BACKEND'], app)
@@ -38,7 +40,7 @@ def initialize_user():
if 'username' in session and 'password' in session:
username = ensure_utf8(session['username'])
- password = ensure_utf8(decrypt_password(session['password']))
+ password = ensure_utf8(session['password'])
try:
g.user = current_app.user_backend.auth(username, password)
except ldap.INVALID_CREDENTIALS:
diff --git a/accounts/default_settings.py b/accounts/default_settings.py
index 09d81f5..4fe53a6 100644
--- a/accounts/default_settings.py
+++ b/accounts/default_settings.py
@@ -5,7 +5,7 @@ DEBUG = False
SECRET_KEY = 'remember to change this to something more random and secret'
# CHANGE THIS! (e.g. os.urandom(32) )
-PASSWORD_ENCRYPTION_KEY = '.\x14\xa7\x1b\xa2:\x1b\xb7\xbck\x1bD w\xab\x87a\xb4\xb7\xca\xf1\x06\xb0\x9f?q\x13\x05\x8dY\xe5<'
+SESSION_ENCRYPTION_KEY = '.\x14\xa7\x1b\xa2:\x1b\xb7\xbck\x1bD w\xab\x87a\xb4\xb7\xca\xf1\x06\xb0\x9f?q\x13\x05\x8dY\xe5<'
MAIL_DEFAULT_SENDER = 'spline accounts <noreply@accounts.spline.de>'
MAIL_REGISTER_NOTIFY = None
diff --git a/accounts/forms.py b/accounts/forms.py
index 4385e7a..9e2e8f3 100644
--- a/accounts/forms.py
+++ b/accounts/forms.py
@@ -5,8 +5,7 @@ from flask.ext.wtf import Form
from wtforms import TextField, PasswordField, ValidationError, BooleanField,\
validators
from functools import partial
-from utils import _username_re, _username_exclude_re, decrypt_password,\
- NotRegexp, url_for
+from utils import _username_re, _username_exclude_re, NotRegexp, url_for
username = partial(TextField, 'Benutzername', [validators.Regexp(_username_re,
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()
diff --git a/accounts/views/default/__init__.py b/accounts/views/default/__init__.py
index c2099d5..52edb10 100644
--- a/accounts/views/default/__init__.py
+++ b/accounts/views/default/__init__.py
@@ -133,7 +133,7 @@ def lost_password_complete(token):
current_app.user_backend.update(user, as_admin=True)
session['username'] = username
- session['password'] = encrypt_password(form.password.data)
+ session['password'] = form.password.data
flash(u'Passwort geändert.', 'success')
return redirect(url_for('.settings'))
@@ -179,7 +179,7 @@ def settings():
if form.password.data:
g.user.change_password(form.password.data, form.old_password.data)
- session['password'] = encrypt_password(form.password.data)
+ session['password'] = form.password.data
flash(u'Passwort geändert', 'success')
changed = True
diff --git a/requirements.txt b/requirements.txt
index 62e9864..ea8476c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,5 +4,6 @@ Flask-Script
Werkzeug>=0.6
Jinja2>=2.4
WTForms>=1.0
+itsdangerous
pycrypto
python-ldap