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 <[email protected]> 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 C<log_level>, if you do, we will also log each > validation failure at the chosen level like this: > > $self->log->$loglevel("Query validation failed: $@"); > > L<CGI::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 L<Params::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 > returned by default. C<< validate_query_config >> is usually called in > C<< setup() >>, or a in a project super-class. > > If <log_level> is defined, it will override the the log file provided in > C<< validate_query_config >> and log a validation failure at that log > evel > > If you set a default, the query will be modified with the new value. > > =cut > > sub validate_query { > > } > > =head2 IMPLENTATION NOTES > > We set "local $Params::Validate::NO_VALIDATION = 0;" to be sure that > Params::Validate works for us, even if is globally disabled. > > To alter the application flow when validation fails, we set > 'error_mode()' at the last minute, and then die, so the error mode is > triggered. Other uses of error_mode() should continue to work as normal. > > This module is intended to be use for simple query validation tasks, > such as a link with query string with a small number of arguments. For > larger validation tasks, especially for processing for submissions using > L< Data::FormValidator > is recommended, along with L< > CGI::Application::ValidateRM > if you using CGI::Application. > > =head2 FUTURE > > This concept could be extended to all check values set through > $self->param(), or through $ENV{PATH_INFO} . > > This plugin does handle file upload validations, and won't in the > future. > > Providing untainting is not a goal of this module, but if it's easy and > someone else provides a patch, perhaps support will be added. > > =head1 AUTHOR > > Mark Stosberg C<< [email protected] >> > > > > ##### CGI::Application community mailing list ################ > ## ## > ## To unsubscribe, or change your message delivery options, ## > ## visit: http://www.erlbaum.net/mailman/listinfo/cgiapp ## > ## ## > ## Web archive: http://www.erlbaum.net/pipermail/cgiapp/ ## > ## Wiki: http://cgiapp.erlbaum.net/ ## > ## ## > ################################################################ > > -- . . . . . . . . . . . . . . . . . . . . . . . . . . . Mark Stosberg Principal Developer [email protected] Summersault, LLC 765-939-9301 ext 202 database driven websites . . . . . http://www.summersault.com/ . . . . . . . . ##### CGI::Application community mailing list ################ ## ## ## To unsubscribe, or change your message delivery options, ## ## visit: http://www.erlbaum.net/mailman/listinfo/cgiapp ## ## ## ## Web archive: http://www.erlbaum.net/pipermail/cgiapp/ ## ## Wiki: http://cgiapp.erlbaum.net/ ## ## ## ################################################################
