From: Darian Moody Date: Sun, 6 Mar 2011 02:39:37 +0000 (+0000) Subject: Fixed security hole - redirects via the next param are now properly sanitized to... X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=32a67fa2a751b59d6c775f8f201981c4a27b2610;p=django-social-auth.git Fixed security hole - redirects via the next param are now properly sanitized to disallow redirecting to external hosts; also DRY'd up views file a little. --- diff --git a/social_auth/utils.py b/social_auth/utils.py new file mode 100644 index 0000000..1c8744d --- /dev/null +++ b/social_auth/utils.py @@ -0,0 +1,18 @@ +import urlparse + +def sanitize_redirect(host, redirect_to): + """ + Given the hostname and an untrusted URL to redirect to, + this method tests it to make sure it isn't garbage/harmful + and returns it, else returns None. + + See http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/views.py#L36 + """ + # Quick sanity check. + if not redirect_to: + return None + netloc = urlparse.urlparse(redirect_to)[1] + # Heavier security check -- don't allow redirection to a different host. + if netloc and netloc != host: + return None + return redirect_to diff --git a/social_auth/views.py b/social_auth/views.py index b83095c..cfe29c3 100644 --- a/social_auth/views.py +++ b/social_auth/views.py @@ -1,21 +1,23 @@ """Views""" from django.conf import settings -from django.http import HttpResponseRedirect, HttpResponse, \ - HttpResponseServerError +from django.http import HttpResponseRedirect, HttpResponse, HttpResponseServerError from django.core.urlresolvers import reverse from django.db import transaction from django.contrib.auth import login, REDIRECT_FIELD_NAME from django.contrib.auth.decorators import login_required from social_auth.backends import get_backend +from social_auth.utils import sanitize_redirect + + +DEFAULT_REDIRECT = getattr(settings, 'LOGIN_REDIRECT_URL', '') def auth(request, backend): """Start authentication process""" complete_url = getattr(settings, 'SOCIAL_AUTH_COMPLETE_URL_NAME', 'complete') - redirect = getattr(settings, 'LOGIN_REDIRECT_URL', '') - return auth_process(request, backend, complete_url, redirect) + return auth_process(request, backend, complete_url) @transaction.commit_on_success @@ -47,8 +49,7 @@ def complete_process(request, backend): social_user = user.social_auth.get(provider=backend_name) if social_user.expiration_delta(): request.session.set_expiry(social_user.expiration_delta()) - url = request.session.pop(REDIRECT_FIELD_NAME, '') or \ - getattr(settings, 'LOGIN_REDIRECT_URL', '') + url = request.session.pop(REDIRECT_FIELD_NAME, '') or DEFAULT_REDIRECT else: url = getattr(settings, 'LOGIN_ERROR_URL', settings.LOGIN_URL) return HttpResponseRedirect(url) @@ -59,8 +60,7 @@ def associate(request, backend): """Authentication starting process""" complete_url = getattr(settings, 'SOCIAL_AUTH_ASSOCIATE_URL_NAME', 'associate_complete') - redirect = getattr(settings, 'LOGIN_REDIRECT_URL', '') - return auth_process(request, backend, complete_url, redirect) + return auth_process(request, backend, complete_url) @login_required @@ -70,8 +70,7 @@ def associate_complete(request, backend): if not backend: return HttpResponseServerError('Incorrect authentication service') backend.auth_complete(user=request.user) - url = request.session.pop(REDIRECT_FIELD_NAME, '') or \ - getattr(settings, 'LOGIN_REDIRECT_URL', '') + url = request.session.pop(REDIRECT_FIELD_NAME, '') or DEFAULT_REDIRECT return HttpResponseRedirect(url) @@ -82,20 +81,21 @@ def disconnect(request, backend): if not backend: return HttpResponseServerError('Incorrect authentication service') backend.disconnect(request.user) - url = request.REQUEST.get(REDIRECT_FIELD_NAME, '') or \ - getattr(settings, 'LOGIN_REDIRECT_URL', '') + url = request.REQUEST.get(REDIRECT_FIELD_NAME, '') or DEFAULT_REDIRECT return HttpResponseRedirect(url) -def auth_process(request, backend, complete_url_name, default_final_url): +def auth_process(request, backend, complete_url_name, + default_redirect=DEFAULT_REDIRECT): """Authenticate using social backend""" redirect = reverse(complete_url_name, args=(backend,)) backend = get_backend(backend, request, redirect) if not backend: return HttpResponseServerError('Incorrect authentication service') data = request.REQUEST - request.session[REDIRECT_FIELD_NAME] = data.get(REDIRECT_FIELD_NAME, - default_final_url) + # Check and sanitize a user-defined GET/POST redirect_to field value. + redirect = sanitize_redirect(request.get_host(), data.get(REDIRECT_FIELD_NAME)) + request.session[REDIRECT_FIELD_NAME] = redirect or DEFAULT_REDIRECT if backend.uses_redirect: return HttpResponseRedirect(backend.auth_url()) else: