Ticket #14614: 14614.obfuscate-request-parameters.diff

File 14614.obfuscate-request-parameters.diff, 13.8 KB (added by Julien Phalip, 13 years ago)
  • django/contrib/auth/views.py

    diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py
    index cfb2659..ded6fe4 100644
    a b from django.http import HttpResponseRedirect, QueryDict  
    66from django.template.response import TemplateResponse
    77from django.utils.http import base36_to_int
    88from django.utils.translation import ugettext as _
     9from django.views.decorators.logging import hide_parameters
    910from django.views.decorators.cache import never_cache
    1011from django.views.decorators.csrf import csrf_protect
    1112
    from django.contrib.auth.models import User  
    1718from django.contrib.auth.tokens import default_token_generator
    1819from django.contrib.sites.models import get_current_site
    1920
    20 
     21@hide_parameters(POST=['password'])
    2122@csrf_protect
    2223@never_cache
    2324def login(request, template_name='registration/login.html',
    def password_reset_done(request,  
    175176                            current_app=current_app)
    176177
    177178# Doesn't need csrf_protect since no-one can guess the URL
     179@hide_parameters(POST=['new_password1', 'new_password2'])
    178180@never_cache
    179181def password_reset_confirm(request, uidb36=None, token=None,
    180182                           template_name='registration/password_reset_confirm.html',
    def password_reset_complete(request,  
    227229    return TemplateResponse(request, template_name, context,
    228230                            current_app=current_app)
    229231
     232@hide_parameters(POST=['old_password', 'new_password1', 'new_password2'])
    230233@csrf_protect
    231234@login_required
    232235def password_change(request,
  • django/core/handlers/base.py

    diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py
    index f216886..6ccf5b2 100644
    a b class BaseHandler(object):  
    206206            exc_info=exc_info,
    207207            extra={
    208208                'status_code': 500,
    209                 'request':request
     209                'request': request
    210210            }
    211211        )
    212212
  • django/core/handlers/modpython.py

    diff --git a/django/core/handlers/modpython.py b/django/core/handlers/modpython.py
    index f0c7701..eb47a9f 100644
    a b class ModPythonRequest(http.HttpRequest):  
    4444        self._post_parse_error = False
    4545        self._stream = self._req
    4646        self._read_started = False
     47        self.hidden_GET_parameters = []
     48        self.hidden_POST_parameters = []
    4749
    4850    def __repr__(self):
    4951        # Since this is called as part of error handling, we need to be very
  • django/core/handlers/wsgi.py

    diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py
    index 434f91c..54527ed 100644
    a b class WSGIRequest(http.HttpRequest):  
    156156        else:
    157157            self._stream = self.environ['wsgi.input']
    158158        self._read_started = False
     159        self.hidden_GET_parameters = []
     160        self.hidden_POST_parameters = []
    159161
    160162    def __repr__(self):
    161163        # Since this is called as part of error handling, we need to be very
    162164        # robust against potentially malformed input.
    163165        try:
    164             get = pformat(self.GET)
     166            get = pformat(self.safe_GET)
    165167        except:
    166168            get = '<could not parse>'
    167169        if self._post_parse_error:
    168170            post = '<could not parse>'
    169171        else:
    170172            try:
    171                 post = pformat(self.POST)
     173                post = pformat(self.safe_POST)
    172174            except:
    173175                post = '<could not parse>'
    174176        try:
    class WSGIRequest(http.HttpRequest):  
    176178        except:
    177179            cookies = '<could not parse>'
    178180        try:
    179             meta = pformat(self.META)
     181            meta = pformat(self.safe_META)
    180182        except:
    181183            meta = '<could not parse>'
    182184        return '<WSGIRequest\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % \
  • django/http/__init__.py

    diff --git a/django/http/__init__.py b/django/http/__init__.py
    index 0d28ec0..5fc7bb6 100644
    a b class HttpRequest(object):  
    144144        self.path = ''
    145145        self.path_info = ''
    146146        self.method = None
     147        self.hidden_GET_parameters = []
     148        self.hidden_POST_parameters = []
    147149
    148150    def __repr__(self):
    149151        return '<HttpRequest\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % \
    150             (pformat(self.GET), pformat(self.POST), pformat(self.COOKIES),
    151             pformat(self.META))
    152 
     152            (pformat(self.safe_GET),
     153             pformat(self.safe_POST),
     154             pformat(self.COOKIES),
     155             pformat(self.safe_META))
     156
     157    @property
     158    def safe_GET(self):
     159        if settings.DEBUG:
     160            return self.GET
     161        result = self.GET.copy()
     162        for param in self.hidden_GET_parameters:
     163            if result.has_key(param):
     164                result[param] = u'********************'
     165        return result
     166   
     167    @property
     168    def safe_POST(self):
     169        if settings.DEBUG:
     170            return self.POST
     171        result = self.POST.copy()
     172        for param in self.hidden_POST_parameters:
     173            if result.has_key(param):
     174                result[param] = u'********************'
     175        return result
     176   
     177    @property
     178    def safe_META(self):
     179        if settings.DEBUG:
     180            return self.META
     181        result = self.META.copy()
     182        result['QUERY_STRING'] = self.safe_GET.urlencode(safe='*')
     183        return result
     184   
    153185    def get_host(self):
    154186        """Returns the HTTP host using the environment or request headers."""
    155187        # We try three options, in order of decreasing preference.
  • django/views/debug.py

    diff --git a/django/views/debug.py b/django/views/debug.py
    index 67f25b3..be81988 100644
    a b import os  
    33import re
    44import sys
    55import types
     6import functools
    67
    78from django.conf import settings
    89from django.http import HttpResponse, HttpResponseServerError, HttpResponseNotFound
    from django.template.defaultfilters import force_escape, pprint  
    1213from django.utils.html import escape
    1314from django.utils.importlib import import_module
    1415from django.utils.encoding import smart_unicode, smart_str
     16from django.core.exceptions import ImproperlyConfigured
    1517
    1618HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST|SIGNATURE')
    1719
    TECHNICAL_500_TEMPLATE = """  
    425427{% if request %}
    426428    <tr>
    427429      <th>Request Method:</th>
    428       <td>{{ request.META.REQUEST_METHOD }}</td>
     430      <td>{{ request.safe_META.REQUEST_METHOD }}</td>
    429431    </tr>
    430432    <tr>
    431433      <th>Request URL:</th>
    Exception Value: {{ exception_value|force_escape }}  
    621623
    622624{% if request %}
    623625  <h3 id="get-info">GET</h3>
    624   {% if request.GET %}
     626  {% if request.safe_GET %}
    625627    <table class="req">
    626628      <thead>
    627629        <tr>
    Exception Value: {{ exception_value|force_escape }}  
    630632        </tr>
    631633      </thead>
    632634      <tbody>
    633         {% for var in request.GET.items %}
     635        {% for var in request.safe_GET.items %}
    634636          <tr>
    635637            <td>{{ var.0 }}</td>
    636638            <td class="code"><pre>{{ var.1|pprint }}</pre></td>
    Exception Value: {{ exception_value|force_escape }}  
    643645  {% endif %}
    644646
    645647  <h3 id="post-info">POST</h3>
    646   {% if request.POST %}
     648  {% if request.safe_POST %}
    647649    <table class="req">
    648650      <thead>
    649651        <tr>
    Exception Value: {{ exception_value|force_escape }}  
    652654        </tr>
    653655      </thead>
    654656      <tbody>
    655         {% for var in request.POST.items %}
     657        {% for var in request.safe_POST.items %}
    656658          <tr>
    657659            <td>{{ var.0 }}</td>
    658660            <td class="code"><pre>{{ var.1|pprint }}</pre></td>
    Exception Value: {{ exception_value|force_escape }}  
    717719      </tr>
    718720    </thead>
    719721    <tbody>
    720       {% for var in request.META.items|dictsort:"0" %}
     722      {% for var in request.safe_META.items|dictsort:"0" %}
    721723        <tr>
    722724          <td>{{ var.0 }}</td>
    723725          <td class="code"><pre>{{ var.1|pprint }}</pre></td>
  • new file django/views/decorators/logging.py

    diff --git a/django/views/decorators/logging.py b/django/views/decorators/logging.py
    new file mode 100644
    index 0000000..d696042
    - +  
     1import functools
     2from django.core.exceptions import ImproperlyConfigured
     3
     4def hide_parameters(GET=None, POST=None, all=None):
     5    if callable(GET) and POST is None and all is None:
     6        # The decorator is called directly on the view without `GET`, `POST` or `all` argument.
     7        view = GET
     8        raise ImproperlyConfigured('hide_parameters requires at least one value for `GET`, `POST` or `all` for `%s`.' % view.__name__)
     9    GET = list(GET) if GET else []
     10    POST = list(POST) if POST else []
     11    all = list(all) if all else []
     12    def decorator(view):
     13        @functools.wraps(view)
     14        def wrapper(request, *args, **kwargs):
     15            request.hidden_GET_parameters = GET + all
     16            request.hidden_POST_parameters = POST + all
     17            return view(request, *args, **kwargs)
     18        return wrapper
     19    return decorator
  • tests/regressiontests/views/tests/debug.py

    diff --git a/tests/regressiontests/views/tests/debug.py b/tests/regressiontests/views/tests/debug.py
    index d778311..d98bdd3 100644
    a b  
     1from __future__ import with_statement
    12import inspect
    23import sys
    34
    45from django.conf import settings
     6from django.core.exceptions import ImproperlyConfigured
    57from django.core.files.uploadedfile import SimpleUploadedFile
    68from django.test import TestCase, RequestFactory
    79from django.core.urlresolvers import reverse
    810from django.template import TemplateSyntaxError
    911from django.views.debug import ExceptionReporter
     12from django.views.decorators.logging import hide_parameters
    1013
    1114from regressiontests.views import BrokenException, except_args
     15from regressiontests.views.views import hidden_params
    1216
    1317
    1418class DebugViewTests(TestCase):
    class ExceptionReporterTests(TestCase):  
    8185        self.assertIn('<h2>Request information</h2>', html)
    8286        self.assertNotIn('<p>Request data not supplied</p>', html)
    8387
     88    def test_hidden_parameters(self):
     89        with self.assertRaises(ImproperlyConfigured) as context_manager:
     90            hide_parameters(hidden_params)
     91        self.assertEqual(context_manager.exception.message, 'hide_parameters requires at least one value for `GET`, `POST` or `all` for `hidden_params`.')
     92       
     93        data = {'sausage-key': 'sausage-value',
     94                'baked-beans-key': 'baked-beans-value',
     95                'hash-brown-key': 'hash-brown-value',
     96                'eggs-key': 'eggs-value',
     97                'bacon-key': 'bacon-value',}
     98       
     99        with self.settings(DEBUG=False):
     100            # Using GET
     101            request = self.rf.get('/hidden_params/', data)
     102            response = hidden_params(request)
     103            request_repr = repr(request)
     104            for k, v in data.items():
     105                # All parameters' names can be seen.
     106                self.assertTrue(k in request_repr)
     107            # Only some parameters' values can be seen
     108            self.assertTrue('sausage-value' in request_repr)
     109            self.assertTrue('baked-beans-value' in request_repr)
     110            self.assertTrue('hash-brown-value' in request_repr)
     111            self.assertTrue('eggs-value' not in request_repr)
     112            self.assertTrue('bacon-value' not in request_repr)
     113           
     114            # Using POST
     115            request = self.rf.post('/hidden_params/', data)
     116            response = hidden_params(request)
     117            request_repr = repr(request)
     118            for k, v in data.items():
     119                # All parameters' names can be seen.
     120                self.assertTrue(k in request_repr)
     121            # Only some parameters' values can be seen.
     122            self.assertTrue('sausage-value' in request_repr)
     123            self.assertTrue('baked-beans-value' not in request_repr)
     124            self.assertTrue('hash-brown-value' not in request_repr)
     125            self.assertTrue('eggs-value' in request_repr)
     126            self.assertTrue('bacon-value' not in request_repr)
     127       
     128        with self.settings(DEBUG=True):
     129            # Everything can be seen in DEBUG mode.
     130           
     131            # Using GET
     132            request = self.rf.get('/hidden_params/', data)
     133            response = hidden_params(request)
     134            request_repr = repr(request)
     135            for k, v in data.items():
     136                self.assertTrue(k in request_repr)
     137                self.assertTrue(v in request_repr)
     138           
     139            # Using POST
     140            request = self.rf.post('/hidden_params/', data)
     141            response = hidden_params(request)
     142            request_repr = repr(request)
     143            for k, v in data.items():
     144                self.assertTrue(k in request_repr)
     145                self.assertTrue(v in request_repr)
     146           
    84147    def test_no_request(self):
    85148        "An exception report can be generated without request"
    86149        try:
  • tests/regressiontests/views/views.py

    diff --git a/tests/regressiontests/views/views.py b/tests/regressiontests/views/views.py
    index 11d289f..aea7b7b 100644
    a b from django.core.urlresolvers import get_resolver  
    66from django.shortcuts import render_to_response, render
    77from django.template import Context, RequestContext, TemplateDoesNotExist
    88from django.views.debug import technical_500_response
     9from django.views.decorators.logging import hide_parameters
    910
    1011from regressiontests.views import BrokenException, except_args
    1112
    def raises_template_does_not_exist(request):  
    128129        return render_to_response('i_dont_exist.html')
    129130    except TemplateDoesNotExist:
    130131        return technical_500_response(request, *sys.exc_info())
     132
     133@hide_parameters(POST=('hash-brown-key', 'baked-beans-key'), GET=('eggs-key',), all=('bacon-key',))
     134def hidden_params(request):
     135    return HttpResponse('')
Back to Top