diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py
index cfb2659..ded6fe4 100644
--- a/django/contrib/auth/views.py
+++ b/django/contrib/auth/views.py
@@ -6,6 +6,7 @@ from django.http import HttpResponseRedirect, QueryDict
 from django.template.response import TemplateResponse
 from django.utils.http import base36_to_int
 from django.utils.translation import ugettext as _
+from django.views.decorators.logging import hide_parameters
 from django.views.decorators.cache import never_cache
 from django.views.decorators.csrf import csrf_protect
 
@@ -17,7 +18,7 @@ from django.contrib.auth.models import User
 from django.contrib.auth.tokens import default_token_generator
 from django.contrib.sites.models import get_current_site
 
-
+@hide_parameters(POST=['password'])
 @csrf_protect
 @never_cache
 def login(request, template_name='registration/login.html',
@@ -175,6 +176,7 @@ def password_reset_done(request,
                             current_app=current_app)
 
 # Doesn't need csrf_protect since no-one can guess the URL
+@hide_parameters(POST=['new_password1', 'new_password2'])
 @never_cache
 def password_reset_confirm(request, uidb36=None, token=None,
                            template_name='registration/password_reset_confirm.html',
@@ -227,6 +229,7 @@ def password_reset_complete(request,
     return TemplateResponse(request, template_name, context,
                             current_app=current_app)
 
+@hide_parameters(POST=['old_password', 'new_password1', 'new_password2'])
 @csrf_protect
 @login_required
 def password_change(request,
diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py
index f216886..6ccf5b2 100644
--- a/django/core/handlers/base.py
+++ b/django/core/handlers/base.py
@@ -206,7 +206,7 @@ class BaseHandler(object):
             exc_info=exc_info,
             extra={
                 'status_code': 500,
-                'request':request
+                'request': request
             }
         )
 
diff --git a/django/core/handlers/modpython.py b/django/core/handlers/modpython.py
index f0c7701..eb47a9f 100644
--- a/django/core/handlers/modpython.py
+++ b/django/core/handlers/modpython.py
@@ -44,6 +44,8 @@ class ModPythonRequest(http.HttpRequest):
         self._post_parse_error = False
         self._stream = self._req
         self._read_started = False
+        self.hidden_GET_parameters = []
+        self.hidden_POST_parameters = []
 
     def __repr__(self):
         # Since this is called as part of error handling, we need to be very
diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py
index 434f91c..54527ed 100644
--- a/django/core/handlers/wsgi.py
+++ b/django/core/handlers/wsgi.py
@@ -156,19 +156,21 @@ class WSGIRequest(http.HttpRequest):
         else:
             self._stream = self.environ['wsgi.input']
         self._read_started = False
+        self.hidden_GET_parameters = []
+        self.hidden_POST_parameters = []
 
     def __repr__(self):
         # Since this is called as part of error handling, we need to be very
         # robust against potentially malformed input.
         try:
-            get = pformat(self.GET)
+            get = pformat(self.safe_GET)
         except:
             get = '<could not parse>'
         if self._post_parse_error:
             post = '<could not parse>'
         else:
             try:
-                post = pformat(self.POST)
+                post = pformat(self.safe_POST)
             except:
                 post = '<could not parse>'
         try:
@@ -176,7 +178,7 @@ class WSGIRequest(http.HttpRequest):
         except:
             cookies = '<could not parse>'
         try:
-            meta = pformat(self.META)
+            meta = pformat(self.safe_META)
         except:
             meta = '<could not parse>'
         return '<WSGIRequest\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % \
diff --git a/django/http/__init__.py b/django/http/__init__.py
index 0d28ec0..5fc7bb6 100644
--- a/django/http/__init__.py
+++ b/django/http/__init__.py
@@ -144,12 +144,44 @@ class HttpRequest(object):
         self.path = ''
         self.path_info = ''
         self.method = None
+        self.hidden_GET_parameters = []
+        self.hidden_POST_parameters = []
 
     def __repr__(self):
         return '<HttpRequest\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % \
-            (pformat(self.GET), pformat(self.POST), pformat(self.COOKIES),
-            pformat(self.META))
-
+            (pformat(self.safe_GET),
+             pformat(self.safe_POST),
+             pformat(self.COOKIES),
+             pformat(self.safe_META))
+
+    @property
+    def safe_GET(self):
+        if settings.DEBUG:
+            return self.GET
+        result = self.GET.copy()
+        for param in self.hidden_GET_parameters:
+            if result.has_key(param):
+                result[param] = u'********************'
+        return result
+    
+    @property
+    def safe_POST(self):
+        if settings.DEBUG:
+            return self.POST
+        result = self.POST.copy()
+        for param in self.hidden_POST_parameters:
+            if result.has_key(param):
+                result[param] = u'********************'
+        return result
+    
+    @property
+    def safe_META(self):
+        if settings.DEBUG:
+            return self.META
+        result = self.META.copy()
+        result['QUERY_STRING'] = self.safe_GET.urlencode(safe='*')
+        return result
+    
     def get_host(self):
         """Returns the HTTP host using the environment or request headers."""
         # We try three options, in order of decreasing preference.
diff --git a/django/views/debug.py b/django/views/debug.py
index 67f25b3..be81988 100644
--- a/django/views/debug.py
+++ b/django/views/debug.py
@@ -3,6 +3,7 @@ import os
 import re
 import sys
 import types
+import functools
 
 from django.conf import settings
 from django.http import HttpResponse, HttpResponseServerError, HttpResponseNotFound
@@ -12,6 +13,7 @@ from django.template.defaultfilters import force_escape, pprint
 from django.utils.html import escape
 from django.utils.importlib import import_module
 from django.utils.encoding import smart_unicode, smart_str
+from django.core.exceptions import ImproperlyConfigured
 
 HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST|SIGNATURE')
 
@@ -425,7 +427,7 @@ TECHNICAL_500_TEMPLATE = """
 {% if request %}
     <tr>
       <th>Request Method:</th>
-      <td>{{ request.META.REQUEST_METHOD }}</td>
+      <td>{{ request.safe_META.REQUEST_METHOD }}</td>
     </tr>
     <tr>
       <th>Request URL:</th>
@@ -621,7 +623,7 @@ Exception Value: {{ exception_value|force_escape }}
 
 {% if request %}
   <h3 id="get-info">GET</h3>
-  {% if request.GET %}
+  {% if request.safe_GET %}
     <table class="req">
       <thead>
         <tr>
@@ -630,7 +632,7 @@ Exception Value: {{ exception_value|force_escape }}
         </tr>
       </thead>
       <tbody>
-        {% for var in request.GET.items %}
+        {% for var in request.safe_GET.items %}
           <tr>
             <td>{{ var.0 }}</td>
             <td class="code"><pre>{{ var.1|pprint }}</pre></td>
@@ -643,7 +645,7 @@ Exception Value: {{ exception_value|force_escape }}
   {% endif %}
 
   <h3 id="post-info">POST</h3>
-  {% if request.POST %}
+  {% if request.safe_POST %}
     <table class="req">
       <thead>
         <tr>
@@ -652,7 +654,7 @@ Exception Value: {{ exception_value|force_escape }}
         </tr>
       </thead>
       <tbody>
-        {% for var in request.POST.items %}
+        {% for var in request.safe_POST.items %}
           <tr>
             <td>{{ var.0 }}</td>
             <td class="code"><pre>{{ var.1|pprint }}</pre></td>
@@ -717,7 +719,7 @@ Exception Value: {{ exception_value|force_escape }}
       </tr>
     </thead>
     <tbody>
-      {% for var in request.META.items|dictsort:"0" %}
+      {% for var in request.safe_META.items|dictsort:"0" %}
         <tr>
           <td>{{ var.0 }}</td>
           <td class="code"><pre>{{ var.1|pprint }}</pre></td>
diff --git a/django/views/decorators/logging.py b/django/views/decorators/logging.py
new file mode 100644
index 0000000..d696042
--- /dev/null
+++ b/django/views/decorators/logging.py
@@ -0,0 +1,19 @@
+import functools
+from django.core.exceptions import ImproperlyConfigured
+
+def hide_parameters(GET=None, POST=None, all=None):
+    if callable(GET) and POST is None and all is None:
+        # The decorator is called directly on the view without `GET`, `POST` or `all` argument.
+        view = GET
+        raise ImproperlyConfigured('hide_parameters requires at least one value for `GET`, `POST` or `all` for `%s`.' % view.__name__)
+    GET = list(GET) if GET else []
+    POST = list(POST) if POST else []
+    all = list(all) if all else []
+    def decorator(view):
+        @functools.wraps(view)
+        def wrapper(request, *args, **kwargs):
+            request.hidden_GET_parameters = GET + all
+            request.hidden_POST_parameters = POST + all
+            return view(request, *args, **kwargs)
+        return wrapper
+    return decorator
diff --git a/tests/regressiontests/views/tests/debug.py b/tests/regressiontests/views/tests/debug.py
index d778311..d98bdd3 100644
--- a/tests/regressiontests/views/tests/debug.py
+++ b/tests/regressiontests/views/tests/debug.py
@@ -1,14 +1,18 @@
+from __future__ import with_statement
 import inspect
 import sys
 
 from django.conf import settings
+from django.core.exceptions import ImproperlyConfigured
 from django.core.files.uploadedfile import SimpleUploadedFile
 from django.test import TestCase, RequestFactory
 from django.core.urlresolvers import reverse
 from django.template import TemplateSyntaxError
 from django.views.debug import ExceptionReporter
+from django.views.decorators.logging import hide_parameters
 
 from regressiontests.views import BrokenException, except_args
+from regressiontests.views.views import hidden_params
 
 
 class DebugViewTests(TestCase):
@@ -81,6 +85,65 @@ class ExceptionReporterTests(TestCase):
         self.assertIn('<h2>Request information</h2>', html)
         self.assertNotIn('<p>Request data not supplied</p>', html)
 
+    def test_hidden_parameters(self):
+        with self.assertRaises(ImproperlyConfigured) as context_manager:
+            hide_parameters(hidden_params)
+        self.assertEqual(context_manager.exception.message, 'hide_parameters requires at least one value for `GET`, `POST` or `all` for `hidden_params`.')
+        
+        data = {'sausage-key': 'sausage-value',
+                'baked-beans-key': 'baked-beans-value',
+                'hash-brown-key': 'hash-brown-value',
+                'eggs-key': 'eggs-value',
+                'bacon-key': 'bacon-value',}
+        
+        with self.settings(DEBUG=False):
+            # Using GET
+            request = self.rf.get('/hidden_params/', data)
+            response = hidden_params(request)
+            request_repr = repr(request)
+            for k, v in data.items():
+                # All parameters' names can be seen.
+                self.assertTrue(k in request_repr)
+            # Only some parameters' values can be seen
+            self.assertTrue('sausage-value' in request_repr)
+            self.assertTrue('baked-beans-value' in request_repr)
+            self.assertTrue('hash-brown-value' in request_repr)
+            self.assertTrue('eggs-value' not in request_repr)
+            self.assertTrue('bacon-value' not in request_repr)
+            
+            # Using POST
+            request = self.rf.post('/hidden_params/', data)
+            response = hidden_params(request)
+            request_repr = repr(request)
+            for k, v in data.items():
+                # All parameters' names can be seen.
+                self.assertTrue(k in request_repr)
+            # Only some parameters' values can be seen.
+            self.assertTrue('sausage-value' in request_repr)
+            self.assertTrue('baked-beans-value' not in request_repr)
+            self.assertTrue('hash-brown-value' not in request_repr)
+            self.assertTrue('eggs-value' in request_repr)
+            self.assertTrue('bacon-value' not in request_repr)
+        
+        with self.settings(DEBUG=True):
+            # Everything can be seen in DEBUG mode.
+            
+            # Using GET
+            request = self.rf.get('/hidden_params/', data)
+            response = hidden_params(request)
+            request_repr = repr(request)
+            for k, v in data.items():
+                self.assertTrue(k in request_repr)
+                self.assertTrue(v in request_repr)
+            
+            # Using POST
+            request = self.rf.post('/hidden_params/', data)
+            response = hidden_params(request)
+            request_repr = repr(request)
+            for k, v in data.items():
+                self.assertTrue(k in request_repr)
+                self.assertTrue(v in request_repr)
+            
     def test_no_request(self):
         "An exception report can be generated without request"
         try:
diff --git a/tests/regressiontests/views/views.py b/tests/regressiontests/views/views.py
index 11d289f..aea7b7b 100644
--- a/tests/regressiontests/views/views.py
+++ b/tests/regressiontests/views/views.py
@@ -6,6 +6,7 @@ from django.core.urlresolvers import get_resolver
 from django.shortcuts import render_to_response, render
 from django.template import Context, RequestContext, TemplateDoesNotExist
 from django.views.debug import technical_500_response
+from django.views.decorators.logging import hide_parameters
 
 from regressiontests.views import BrokenException, except_args
 
@@ -128,3 +129,7 @@ def raises_template_does_not_exist(request):
         return render_to_response('i_dont_exist.html')
     except TemplateDoesNotExist:
         return technical_500_response(request, *sys.exc_info())
+
+@hide_parameters(POST=('hash-brown-key', 'baked-beans-key'), GET=('eggs-key',), all=('bacon-key',))
+def hidden_params(request):
+    return HttpResponse('')
