As I considered this problem space further, I'm wondering if I should be
following the approach of the LinkIntegrity plugin instead. It adds a
checksum to internal application links, which allows tampering to be
detected:
http://search.cpan.org/dist/CGI-Application-Plugin-LinkIntegrity/lib/CGI/Application/Plugin/LinkIntegrity.pm
Pros for LinkIntegrity:
- If an query param is missing, you can tell whether it's because the
application failed to send it, or the user tampered with it.
- It's consistent and to use...the syntax is the same for all links.
Cons for LinkIntegrity:
- Adding a required checksum to existing link means to old link quits working,
and backward compatibility may be lost.
- It currently seems to be implemented to require the integrity checks
to be turned on at the module level. I'd like control at the
run-mode level. (I'm sure I could patch or fork to fix this).
I think the conclusion I'm coming to is that the approaches don't need
to be either/or, but rather they complement each other. Only
LinkIntegrity can tell me if a query has been tampered with or not.
However, only validation can check if in fact I have all parameters I
need in the right format. That protects against the case where my
application generates a link with a valid checksum, but somehow has the
wrong data in it. If I skipped validation in the receiving run mode, I
open myself up for a garbage-in/garbage-out problem, or perhaps worse.
Anyone here using LinkIntegrity? How are you doing lightweight query
validation?
Mark
On Fri, 10 Apr 2009 18:13:59 -0400
Mark Stosberg m...@summersault.com wrote:
I'm considering producing a CGI::Application plugin for light weight query
validation using Params::Validate. I've drafted the POD for it, and
that's below.
The implementation would include one novel technical concept that I
haven't notice in use before in a CGI::Application project.
We wanted to clean up the API, so that instead of this:
my $err_page = $self-validate_query;
return $err_page if $err_page
We could just do this:
$self-validate_query;
This means we had to find some other way to interrupt the flow and
return our error page. The solution is to write a suicide note and then
pull the trigger. Or rather, in validate_query(), if validation fails,
we do a just-in-time setting of error_mode(), and then die, which
interrupts the flow and triggers the error_mode to be called.
The solution seems reasonable, as a general error_mode() would still
remain in effect, and if some reason you don't want interrupt the flow,
just trap the exception the usual way:
eval { $self-validate_query; }
I hadn't considered it until now, but perhaps we could also use
Exception::Class so users could easily distinquish between
death-by-validation failure and unexpected-death.
Now that I've thought of this approach, it makes me want to apply to to
the ::ValidateRM interface. Anyone see a reason not to take this
approach for this plugin, or ValidateRM?
=head1 NAME
CGI::Application::Plugin::ValidateQuery
=head1 SYNOPSIS
sub setup {
my $self = shift;
$self-validate_query_config(
# define a page to show for invalid queries, or default to
# serving a plain, internal page
error_mode = 'my_invalid_query_run_mode',
log_level = 'notice',
);
}
sub my_run_mode {
my $self = shift;
# validate the query and return a standard error page on failure.
$self-validate_query(
pet_id= SCALAR,
direction = { type = SCALAR, default = 'up' },
);
# go on with life...
}
=head1 DESCRIPTION
This plugin is for small query validation tasks. For example, perhaps
you link to a page where a pet_id is required, and you need to reality
check that this exists or return essentially a generic error message to
the user.
Even if your application generates the link, it may become altered
through tampering, malware, or other unanticipated events.
This plugin uses Params::Validate to validate the query string. You
can define your own error page to return on failure, or we'll supply a
plain default one internally.
You may also define a Clog_level, if you do, we will also log each
validation failure at the chosen level like this:
$self-log-$loglevel(Query validation failed: $@);
LCGI::Application::Plugin::LogDispatch is one plugin which implements
this logging API.
=head2 validate_query
$self-validate_query(
pet_id = SCALAR,
type = { type = SCALAR, default = 'food' },
log_level = 'critical', # optional
);
Validates C $self-query using LParams::Validate. If any required
query param is missing or invalid, the run mode defined with C
validate_query_config will be used, or a plain internal page will be