Commit 1515eb46 authored by Florian Apolloner's avatar Florian Apolloner
Browse files

[1.3.X] Fixed #18856 -- Ensured that redirects can't be poisoned by malicious users.

parent 6383d235
Loading
Loading
Loading
Loading
+22 −28
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@ from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect, QueryDict
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.utils.http import base36_to_int
from django.utils.http import base36_to_int, is_safe_url
from django.utils.translation import ugettext as _
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect
@@ -33,18 +33,11 @@ def login(request, template_name='registration/login.html',
    if request.method == "POST":
        form = authentication_form(data=request.POST)
        if form.is_valid():
            netloc = urlparse.urlparse(redirect_to)[1]

            # Use default setting if redirect_to is empty
            if not redirect_to:
                redirect_to = settings.LOGIN_REDIRECT_URL

            # Security check -- don't allow redirection to a different
            # host.
            elif netloc and netloc != request.get_host():
            # Ensure the user-originating redirection url is safe.
            if not is_safe_url(url=redirect_to, host=request.get_host()):
                redirect_to = settings.LOGIN_REDIRECT_URL

            # Okay, security checks complete. Log the user in.
            # Okay, security check complete. Log the user in.
            auth_login(request, form.get_user())

            if request.session.test_cookie_worked():
@@ -76,26 +69,27 @@ def logout(request, next_page=None,
    Logs out the user and displays 'You are logged out' message.
    """
    auth_logout(request)
    redirect_to = request.REQUEST.get(redirect_field_name, '')
    if redirect_to:
        netloc = urlparse.urlparse(redirect_to)[1]

    if redirect_field_name in request.REQUEST:
        next_page = request.REQUEST[redirect_field_name]
        # Security check -- don't allow redirection to a different host.
        if not (netloc and netloc != request.get_host()):
            return HttpResponseRedirect(redirect_to)
        if not is_safe_url(url=next_page, host=request.get_host()):
            next_page = request.path

    if next_page:
        # Redirect to this page until the session has been cleared.
        return HttpResponseRedirect(next_page)

    if next_page is None:
    current_site = get_current_site(request)
    context = {
        'site': current_site,
        'site_name': current_site.name,
        'title': _('Logged out')
    }
        context.update(extra_context or {})
    if extra_context is not None:
        context.update(extra_context)
    return render_to_response(template_name, context,
                              context_instance=RequestContext(request, current_app=current_app))
    else:
        # Redirect to this page until the session has been cleared.
        return HttpResponseRedirect(next_page or request.path)

def logout_then_login(request, login_url=None, current_app=None, extra_context=None):
    """
+4 −7
Original line number Diff line number Diff line
@@ -40,9 +40,6 @@ def post_comment(request, next=None, using=None):
        if not data.get('email', ''):
            data["email"] = request.user.email

    # Check to see if the POST data overrides the view's next argument.
    next = data.get("next", next)

    # Look up the object we're trying to comment about
    ctype = data.get("content_type")
    object_pk = data.get("object_pk")
@@ -96,7 +93,7 @@ def post_comment(request, next=None, using=None):
            template_list, {
                "comment": form.data.get("comment", ""),
                "form": form,
                "next": next,
                "next": data.get("next", next),
            },
            RequestContext(request, {})
        )
@@ -127,7 +124,7 @@ def post_comment(request, next=None, using=None):
        request = request
    )

    return next_redirect(data, next, comment_done, c=comment._get_pk_val())
    return next_redirect(request, next, comment_done, c=comment._get_pk_val())

comment_done = confirmation_view(
    template = "comments/posted.html",
+4 −3
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ from django.contrib import comments
from django.contrib.comments import signals
from django.views.decorators.csrf import csrf_protect


@csrf_protect
@login_required
def flag(request, comment_id, next=None):
@@ -23,7 +24,7 @@ def flag(request, comment_id, next=None):
    # Flag on POST
    if request.method == 'POST':
        perform_flag(request, comment)
        return next_redirect(request.POST.copy(), next, flag_done, c=comment.pk)
        return next_redirect(request, next, flag_done, c=comment.pk)

    # Render a form on GET
    else:
@@ -50,7 +51,7 @@ def delete(request, comment_id, next=None):
    if request.method == 'POST':
        # Flag the comment as deleted instead of actually deleting it.
        perform_delete(request, comment)
        return next_redirect(request.POST.copy(), next, delete_done, c=comment.pk)
        return next_redirect(request, next, delete_done, c=comment.pk)

    # Render a form on GET
    else:
@@ -77,7 +78,7 @@ def approve(request, comment_id, next=None):
    if request.method == 'POST':
        # Flag the comment as approved.
        perform_approve(request, comment)
        return next_redirect(request.POST.copy(), next, approve_done, c=comment.pk)
        return next_redirect(request, next, approve_done, c=comment.pk)

    # Render a form on GET
    else:
+6 −4
Original line number Diff line number Diff line
@@ -4,14 +4,15 @@ A few bits of helper functions for comment views.

import urllib
import textwrap
from django.http import HttpResponseRedirect
from django.core import urlresolvers
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.core.exceptions import ObjectDoesNotExist
from django.contrib import comments
from django.utils.http import is_safe_url

def next_redirect(data, default, default_view, **get_kwargs):
def next_redirect(request, default, default_view, **get_kwargs):
    """
    Handle the "where should I go next?" part of comment views.

@@ -21,9 +22,10 @@ def next_redirect(data, default, default_view, **get_kwargs):

    Returns an ``HttpResponseRedirect``.
    """
    next = data.get("next", default)
    if next is None:
    next = request.POST.get('next', default)
    if not is_safe_url(url=next, host=request.get_host()):
        next = urlresolvers.reverse(default_view)

    if get_kwargs:
        if '#' in next:
            tmp = next.rsplit('#', 1)
+12 −0
Original line number Diff line number Diff line
@@ -204,3 +204,15 @@ else:
        """
        p1, p2 = urlparse.urlparse(url1), urlparse.urlparse(url2)
        return p1[0:2] == p2[0:2]

def is_safe_url(url, host=None):
    """
    Return ``True`` if the url is a safe redirection (i.e. it doesn't point to
    a different host).

    Always returns ``False`` on an empty url.
    """
    if not url:
        return False
    netloc = urlparse.urlparse(url)[1]
    return not netloc or netloc == host
Loading