package Mojolicious::Plugin::FormTamperingProtector; use strict; use warnings; use Mojo::Base 'Mojolicious::Plugin'; our $VERSION = '0.03'; use Data::Dumper; use Mojo::JSON qw(decode_json encode_json); use Mojo::Util qw{encode decode xml_escape hmac_sha1_sum secure_compare b64_decode b64_encode}; use HTML::ValidationRules::Legacy qw{validate extract}; our $TERM_ACTION = 0; our $TERM_SCHEMA = 1; ### --- ### register ### --- sub register { my ($self, $app, $opt) = @_; my $schema_key = $opt->{namespace}. "-schema"; my $sess_key = $opt->{namespace}. '-sessid'; my $actions = ref $opt->{action} ? $opt->{action} : [$opt->{action}]; $app->hook(before_dispatch => sub { my $c = shift; my $req = $c->req; if ($req->method eq 'POST' && grep {$_ eq $req->url->path} @$actions) { my $wrapper = deserialize(unsign( $req->param($schema_key), ($c->session($sess_key) || ''). $app->secrets->[0] )); $req->params->remove($schema_key); if (!$wrapper) { return $opt->{blackhole}->($c, 'Form schema is missing, possible hacking attempt'); } if ($req->url->path ne $wrapper->{$TERM_ACTION}) { return $opt->{blackhole}->($c, 'Action attribute has been tampered'); } if (my $err = validate($wrapper->{$TERM_SCHEMA}, $req->params)) { return $opt->{blackhole}->($c, $err); } } }); $app->hook(after_dispatch => sub { my $c = shift; if ($c->res->headers->content_type =~ qr{^text/html} && $c->res->body =~ qr{