File Coverage

blib/lib/HTML/FormHandler/Field/RequestToken.pm
Criterion Covered Total %
statement 35 40 87.5
branch 3 14 21.4
condition n/a
subroutine 12 12 100.0
pod 3 3 100.0
total 53 69 76.8


line stmt bran cond sub pod time code
1             package HTML::FormHandler::Field::RequestToken;
2             $HTML::FormHandler::Field::RequestToken::VERSION = '0.40068';
3 1     1   828 use HTML::FormHandler::Moose;
  1         3  
  1         14  
4             extends 'HTML::FormHandler::Field::Hidden';
5              
6 1     1   2773 use namespace::autoclean;
  1         2  
  1         13  
7              
8 1     1   827 use Crypt::CBC;
  1         3993  
  1         36  
9 1     1   543 use MIME::Base64 qw(decode_base64 encode_base64);
  1         744  
  1         177  
10 1     1   9 use Moose::Util::TypeConstraints qw(class_type);
  1         2  
  1         15  
11 1     1   621 use Try::Tiny;
  1         3  
  1         452  
12              
13             has '+required' => ( default => 1 );
14              
15             has '+default_method' => ( default => sub { \&get_token });
16              
17              
18             has 'expiration_time' => (
19             is => 'rw',
20             default => 3600
21             );
22              
23              
24             has 'token_prefix' => (
25             is => 'rw',
26             default => ''
27             );
28              
29              
30             has 'crypto_key' => (
31             is => 'rw',
32             default => 'rEpLaCeMe',
33             );
34              
35              
36             has 'crypto_cipher_type' => (
37             is => 'rw',
38             default => 'Blowfish',
39             );
40              
41              
42             has 'message' => (
43             is => 'rw',
44             default => 'Form submission failed. Please try again.'
45             );
46              
47              
48             has 'cipher' => (
49             is => 'ro',
50             isa => class_type('Crypt::CBC'),
51             lazy => 1,
52             builder => '_build_cipher',
53             );
54              
55             sub _build_cipher {
56 1     1   5 my ($self) = @_;
57 1         41 return Crypt::CBC->new(
58             -key => $self->crypto_key,
59             -cipher => $self->crypto_cipher_type,
60             -salt => 1,
61             -header => 'salt',
62             );
63             }
64              
65             sub validate {
66 1     1 1 4 my ($self, $value) = @_;
67              
68             # If it's good, return it
69 1 50       9 unless ( $self->verify_token($value) ) {
70 1         12 $self->add_error();
71             }
72              
73             }
74              
75              
76             sub verify_token {
77 1     1 1 3 my ($self, $token) = @_;
78              
79 1 50       6 return undef unless($token);
80              
81 1         29 my $form = $self->form;
82              
83 1         5 my $value = undef;
84             try {
85 1     1   110 $value = $self->cipher->decrypt(decode_base64($token));
86 0 0       0 if ( my $prefix = $self->token_prefix ) {
87 0 0       0 return undef unless ($value =~ s/^\Q$prefix\E//);
88             }
89 1     1   17 } catch {};
90              
91 1 50       14 return undef unless defined($value);
92 0 0       0 return undef unless ( $value =~ /^\d+$/ );
93 0 0       0 return undef if ( time() > $value );
94              
95 0         0 return 1;
96             }
97              
98              
99             sub get_token {
100 1     1 1 3 my $self = shift;
101              
102 1         36 my $value = $self->token_prefix . (time() + $self->expiration_time);
103 1         42 my $token = encode_base64($self->cipher->encrypt($value));
104 1         1193 $token =~ s/[\s\r\n]+//g;
105 1         8 return $token;
106             }
107              
108             __PACKAGE__->meta->make_immutable;
109              
110             1;
111              
112             __END__
113              
114             =pod
115              
116             =encoding UTF-8
117              
118             =head1 NAME
119              
120             HTML::FormHandler::Field::RequestToken
121              
122             =head1 VERSION
123              
124             version 0.40068
125              
126             =head1 SYNOPSIS
127              
128             with 'HTML::FormHandler::Field::Role::RequestToken';
129             ...
130             has_field '_token' => (
131             type => 'RequestToken',
132             );
133              
134             =head1 DESCRIPTION
135              
136             This field is for preventing CSRF attacks. It contains
137             an encrypted token containing an expiration time for the form.
138             No data needs to be persisted in the user's session or on the
139             server.
140              
141             =head1 NAME
142              
143             HTML::FormHandler::Field::RequestToken - Hidden text field which contains
144             a unique time-stamped token
145              
146             =head1 ATTRIBUTES
147              
148             =head2 expiration_time
149              
150             Length of time (in seconds) that token will be accepted as valid from
151             the time it is initially generated. Defaults to C<3600>.
152              
153             =head2 token_prefix
154              
155             An optional string to prepend to the token value before encrypting it.
156             If specified, any received tokens must begin with this value to be
157             accepted as valid. Defaults to an empty string.
158              
159             Passed on form process. C<< $c->sessionid . '|' >>
160              
161             =head2 crypto_key
162              
163             Key to use to encrypt/decrypt the token payload.
164              
165             =head2 crypto_cipher_type
166              
167             The C<Crypt::CBC> cipher to use to encrypt/decrypt the token payload.
168             Defaults to C<Blowfish>.
169              
170             =head2 message
171              
172             Error message if token is missing/invalid.
173              
174             =head2 cipher
175              
176             A C<Crypt::CBC> object to handle encrypting/decrypting the token payload.
177             If not specified, L</crypto_key> and L</crypto_cipher_type> will be
178             used to construct one.
179              
180             =head2 verify_token
181              
182             Validates whether the specified token is currently valid for this form.
183              
184             =head2 get_token
185              
186             Generates a new token and returns it.
187              
188             =head1 AUTHOR
189              
190             FormHandler Contributors - see HTML::FormHandler
191              
192             =head1 COPYRIGHT AND LICENSE
193              
194             This software is copyright (c) 2017 by Gerda Shank.
195              
196             This is free software; you can redistribute it and/or modify it under
197             the same terms as the Perl 5 programming language system itself.
198              
199             =cut