diff --git a/lib/Catalyst.pm b/lib/Catalyst.pm
index 9a886e6..da26947 100644
--- a/lib/Catalyst.pm
+++ b/lib/Catalyst.pm
@@ -1913,8 +1913,9 @@ sub prepare_body {
 
     if ( $c->debug && keys %{ $c->req->body_parameters } ) {
         my $t = Text::SimpleTable->new( [ 35, 'Parameter' ], [ 36, 'Value' ] );
-        for my $key ( sort keys %{ $c->req->body_parameters } ) {
-            my $param = $c->req->body_parameters->{$key};
+        my $filtered_params = $c->apply_parameter_debug_filters('body', $c->req->body_parameters);
+        for my $key ( sort keys %$filtered_params ) {
+            my $param = $filtered_params->{$key};
             my $value = defined($param) ? $param : '';
             $t->row( $key,
                 ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
@@ -2007,8 +2008,9 @@ sub prepare_query_parameters {
 
     if ( $c->debug && keys %{ $c->request->query_parameters } ) {
         my $t = Text::SimpleTable->new( [ 35, 'Parameter' ], [ 36, 'Value' ] );
-        for my $key ( sort keys %{ $c->req->query_parameters } ) {
-            my $param = $c->req->query_parameters->{$key};
+        my $filtered_params = $c->apply_parameter_debug_filters('query', $c->req->query_parameters);
+        for my $key ( sort keys %{ $filtered_params } ) {
+            my $param = $filtered_params->{$key};
             my $value = defined($param) ? $param : '';
             $t->row( $key,
                 ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
@@ -2017,6 +2019,173 @@ sub prepare_query_parameters {
     }
 }
 
+=head2 $c->apply_parameter_debug_filters($params)
+
+Applies configured filters to parameter list for debug output.
+
+If you have sensitive data that you do not want written to the Catalyst
+debug logs, you can set options in your config to filter those values out.
+There are a few different ways you can set these up depending on what
+exactly you need to filter.
+
+=head3 Filtering parameters by name
+
+The most basic means of filtering is to add an entry into your config
+as shown below.  You can have a simple scalar to just filter a
+single parameter or an ARRAY ref to filter out multiple params.
+
+    # filters a single param
+    __PACKAGE__->config->{debug}->{filter_params} = 'param_name';
+
+    # filters multiple params
+    __PACKAGE__->config->{debug}->{filter_params} = [qw(param1 param2)];
+
+When the debug logs are generated for a given request, any parameters
+(query or body) that exactly match the specified value(s) will have
+their values replaced with '[FILTERED]'.  For instance:
+
+    [debug] Query Parameters are:
+    .-------------------------------------+--------------------------------------.
+    | Parameter                           | Value                                |
+    +-------------------------------------+--------------------------------------+
+    | param_name                          | [FILTERED]                           |
+    .-------------------------------------+--------------------------------------.
+
+=head3 Filtering parameters by regular expression
+
+If you have a set of parameters you need to filter, you can specify a
+regular expression that will be used to match against parameter names.
+
+    # filters parameters starting with "private."
+    __PACKAGE__->config->{debug}->{filter_params} = qr/^private\./
+
+    # filters parameters named "param1" or starting with "private." or "secret."
+    __PACKAGE__->config->{debug}->{filter_params} = [ 'param1', qr/^private\./, qr/^secret\./ ]
+
+Notice on the second example, the ARRAY ref contains a string as well
+as two regular expressions.  This should DWIM and filter parameters that
+match any of the filters specified.
+
+=head3 Filtering parameters by callback
+
+If you want even more flexible filtering, you can specify an anonymous
+subroutine.  The subroutine is given the parameter name and value and
+is expected to return the new value that will be show in the debug log.
+An C<undef> return value indicates that no change should be made to
+the value.
+
+    # transform any "password" param to "********"
+    __PACKAGE__->config->{debug}->{filter_params} =
+        sub { my ( $k, $v ) = @_; return unless $k eq 'password'; return '*' x 8; };
+
+    # combine with other filtering methods
+    __PACKAGE__->config->{debug}->{filter_params} = [
+        'simple_param_name',
+        qr/^private\./,
+        sub { my ( $k, $v ) = @_; return unless $k eq 'password'; return '*' x 8; },
+    ];
+
+An example of the debug log for a request with 
+C<password=secret&some_other_param=some_other_value> would be:
+
+    [debug] Body Parameters are:
+    .-------------------------------------+--------------------------------------.
+    | Parameter                           | Value                                |
+    +-------------------------------------+--------------------------------------+
+    | some_other_param                    | some_other_value                     |
+    | password                            | ********                             |
+    .-------------------------------------+--------------------------------------.
+
+=head3 Filtering by parameter location
+
+If you have a different set of filters based on how they were passed
+(query vs. body vs. all), you can specify a HASH ref with different sets of
+filters:
+
+    # filters all body parameters
+    __PACKAGE__->config->{debug}->{filter_params}->{body} = qr//;
+
+    # filters query parameters starting with "private."
+    __PACKAGE__->config->{debug}->{filter_params}->{query} = qr/^private\./
+
+    # filters all parameters (query or body) through the specified callback
+    __PACKAGE__->config->{debug}->{filter_params}->{all} = sub { return unless $_[0] eq 'fizzbuzz'; return 'FIZZBUZZ FILTER' };
+
+Of course, you can use any of the above filtering methods with these
+"location-specific" filters:
+
+    # body parameter filters
+    __PACKAGE__->config->{debug}->{filter_params}->{body} = [
+        'some_param',
+        qr/^private\./,
+        sub { return 'XXX' if shift eq 'other_param' }
+    ];
+
+    # query parameter filters
+    __PACKAGE__->config->{debug}->{filter_params}->{query} = 'some_query_param';
+
+    # query parameter filters
+    __PACKAGE__->config->{debug}->{filter_params}->{all} = [qw(foo bar)];
+
+=cut
+
+sub apply_parameter_debug_filters {
+    my $c = shift;
+    my $type = shift;
+    my $params = shift;
+
+    # take a copy since we don't want to modify the original
+    my $filtered = {%$params};
+
+	my $filter_param_config = $c->config->{Debug}->{filter_params};
+    my @filters;
+    if(ref($filter_param_config) eq 'HASH'){
+        # filters broken out by parameter type (i.e. body, query, all)
+		my $type_filters = $filter_param_config->{$type} || [];
+		$type_filters = [$type_filters] if ref $type_filters ne 'ARRAY';
+		my $all_filters = $filter_param_config->{'all'} || [];
+		$all_filters = [$all_filters] if ref $all_filters ne 'ARRAY';
+
+        @filters = ( @$type_filters, @$all_filters );
+    } elsif(ref($filter_param_config) eq 'ARRAY'){
+        # standard filters, applied to any parameter type
+        @filters = @{$filter_param_config};
+    } elsif($filter_param_config) {
+        # a single filter
+        @filters = ($filter_param_config);
+    }
+
+    my $filter_str = '[FILTERED]';
+
+    # apply filters to each param
+    foreach my $f( @filters){
+
+        if(!ref($f)){
+            # simple key lookup
+            $filtered->{$f} = $filter_str if(exists($filtered->{$f}));
+        } elsif(ref($f) eq 'Regexp'){
+            # match on regex
+            foreach my $k(grep {$_ =~ $f} keys %$filtered){
+                $filtered->{$k} = $filter_str;
+            }
+        } elsif(ref($f) eq 'CODE'){
+            # allow callback to modify each parameter
+            foreach my $k(keys %$filtered){
+                # take a copy of the key to avoid the callback inadvertantly
+                # modifying things
+                my $copy_key = $k;
+
+                my $returned = $f->($copy_key => $filtered->{$k});
+
+                # if no value is returned, we assume the filter chose not to modify anything
+                # otherwise, the returned value is the logged value
+                $filtered->{$k} = $returned if defined $returned;
+            }
+        }
+    }
+    return $filtered;
+}
+
 =head2 $c->prepare_read
 
 Prepares the input for reading.
diff --git a/t/unit_core_apply_param_filters.t b/t/unit_core_apply_param_filters.t
new file mode 100644
index 0000000..b2f9ce2
--- /dev/null
+++ b/t/unit_core_apply_param_filters.t
@@ -0,0 +1,47 @@
+use strict;
+use warnings;
+use Test::More tests=>12;
+
+use Catalyst;
+my $context = Catalyst->new( {} );
+$context->config->{debug}->{filter_params} = 'simple_str';
+
+isa_ok( $context, 'Catalyst' );
+my $params = $context->apply_parameter_debug_filters( 'query', {} );
+is_deeply( $params, {}, 'empty param list' );
+my $filter_str = '[FILTERED]';
+
+$params = $context->apply_parameter_debug_filters( 'body', { simple_str => 1, other_str => 2 } );
+is( $params->{simple_str}, $filter_str, 'filtered simple_str' );
+is( $params->{other_str},  '2',         "didn't filter other_str" );
+
+$context->config->{debug}->{filter_params} = [qw(a b)];
+$params = $context->apply_parameter_debug_filters( 'query', { a => 1, b => 2, c => 3 }, );
+
+is_deeply( $params, { a => $filter_str, b => $filter_str, c => 3 }, 'list of simple param names' );
+
+$context->config->{debug}->{filter_params} = qr/^foo/;
+$params = $context->apply_parameter_debug_filters( 'query', { foo => 1, foobar => 2, c => 3 }, );
+is_deeply( $params, { foo => $filter_str, foobar => $filter_str, c => 3 }, 'single regex' );
+
+$context->config->{debug}->{filter_params} = [qr/^foo/, qr/bar/, 'simple'];
+$params = $context->apply_parameter_debug_filters( 'query', { foo => 1, foobar => 2, bar => 3, c => 3, simple => 4 }, );
+is_deeply( $params, { foo => $filter_str, foobar => $filter_str, bar => $filter_str, c => 3, simple => $filter_str }, 'array of regexes and a simple filter' );
+
+$context->config->{debug}->{filter_params} = sub { return unless shift eq 'password'; return '*' x 8 };
+$params = $context->apply_parameter_debug_filters( 'query', { password => 'secret', other => 'public' }, );
+is_deeply( $params, { other => 'public', password => '********' }, 'single CODE ref' );
+
+$context->config->{debug}->{filter_params} = {};
+$context->config->{debug}->{filter_params}->{body} = qr//;
+$params = $context->apply_parameter_debug_filters( 'query', { a=>1, b=>2 } );
+is_deeply( $params, { a=>1, b=>2 }, 'body filters do not modify query params' );
+$params = $context->apply_parameter_debug_filters( 'body', { a=>1, b=>2 } );
+is_deeply( $params, { a => $filter_str, b => $filter_str }, 'all body params filtered' );
+
+delete($context->config->{debug}->{filter_params}->{body});
+$context->config->{debug}->{filter_params}->{all} = [qw(foo bar)];
+$params = $context->apply_parameter_debug_filters( 'body', { foo=>1, bar=>2, baz=>3 } );
+is_deeply( $params, { foo => $filter_str, bar => $filter_str, baz => 3 }, 'using the "all" type filter on body params' );
+$params = $context->apply_parameter_debug_filters( 'query', { foo=>1, bar=>2, baz=>3 } );
+is_deeply( $params, { foo => $filter_str, bar => $filter_str, baz => 3 }, 'using the "all" type filter on query params' );
-- 
1.6.2.4

