Update community authentication to pass an arbitrary datablock instead of url

This makes it possible to pass URLs that will fail when they end up being double
escaped in some cases, since they contain non-url-safe characters. Instead, they'd
be base64-encoded, and thus safe.

Also update the django community auth provider to do just this, including encrypting
the data with the site secret key to make sure it can't be changed/injected by
tricking the user to go directly to the wrong URL.
This commit is contained in:
Magnus Hagander
2013-06-20 15:16:47 +02:00
parent bd539a392e
commit 78de94d17c
3 changed files with 69 additions and 18 deletions

View File

@ -27,8 +27,10 @@ from django.conf import settings
import base64
import urlparse
from urllib import quote_plus
import urllib
from Crypto.Cipher import AES
from Crypto.Hash import SHA
from Crypto import Random
import time
class AuthBackend(ModelBackend):
@ -45,9 +47,20 @@ class AuthBackend(ModelBackend):
# Handle login requests by sending them off to the main site
def login(request):
if request.GET.has_key('next'):
return HttpResponseRedirect("%s?su=%s" % (
# Put together an url-encoded dict of parameters we're getting back,
# including a small nonce at the beginning to make sure it doesn't
# encrypt the same way every time.
s = "t=%s&%s" % (int(time.time()), urllib.urlencode({'r': request.GET['next']}))
# Now encrypt it
r = Random.new()
iv = r.read(16)
encryptor = AES.new(SHA.new(settings.SECRET_KEY).digest()[:16], AES.MODE_CBC, iv)
cipher = encryptor.encrypt(s + ' ' * (16-(len(s) % 16))) # pad to 16 bytes
return HttpResponseRedirect("%s?d=%s$%s" % (
settings.PGAUTH_REDIRECT,
quote_plus(request.GET['next']),
base64.b64encode(iv, "-_"),
base64.b64encode(cipher, "-_"),
))
else:
return HttpResponseRedirect(settings.PGAUTH_REDIRECT)
@ -119,9 +132,21 @@ def auth_receive(request):
user.backend = "%s.%s" % (AuthBackend.__module__, AuthBackend.__name__)
django_login(request, user)
# Finally, redirect the user
if data.has_key('su'):
return HttpResponseRedirect(data['su'][0])
# Finally, check of we have a data package that tells us where to
# redirect the user.
if data.has_key('d'):
(ivs, datas) = data['d'][0].split('$')
decryptor = AES.new(SHA.new(settings.SECRET_KEY).digest()[:16],
AES.MODE_CBC,
base64.b64decode(ivs, "-_"))
s = decryptor.decrypt(base64.b64decode(datas, "-_")).rstrip(' ')
try:
rdata = urlparse.parse_qs(s, strict_parsing=True)
except ValueError, e:
raise Exception("Invalid encrypted data received.")
if rdata.has_key('r'):
# Redirect address
return HttpResponseRedirect(rdata['r'][0])
# No redirect specified, see if we have it in our settings
if hasattr(settings, 'PGAUTH_REDIRECT_SUCCESS'):
return HttpResponseRedirect(settings.PGAUTH_REDIRECT_SUCCESS)