line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Mojolicious::Plugin::CSRFDefender; |
2
|
|
|
|
|
|
|
|
3
|
6
|
|
|
6
|
|
39355
|
use strict; |
|
6
|
|
|
|
|
17
|
|
|
6
|
|
|
|
|
214
|
|
4
|
6
|
|
|
6
|
|
35
|
use warnings; |
|
6
|
|
|
|
|
9
|
|
|
6
|
|
|
|
|
184
|
|
5
|
6
|
|
|
6
|
|
34
|
use Carp; |
|
6
|
|
|
|
|
13
|
|
|
6
|
|
|
|
|
618
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = '0.0.8'; |
8
|
|
|
|
|
|
|
|
9
|
6
|
|
|
6
|
|
34
|
use base qw(Mojolicious::Plugin Class::Accessor::Fast); |
|
6
|
|
|
|
|
10
|
|
|
6
|
|
|
|
|
5625
|
|
10
|
|
|
|
|
|
|
__PACKAGE__->mk_accessors(qw( |
11
|
|
|
|
|
|
|
parameter_name |
12
|
|
|
|
|
|
|
session_key |
13
|
|
|
|
|
|
|
token_length |
14
|
|
|
|
|
|
|
error_status |
15
|
|
|
|
|
|
|
error_content |
16
|
|
|
|
|
|
|
error_template |
17
|
|
|
|
|
|
|
onetime |
18
|
|
|
|
|
|
|
)); |
19
|
|
|
|
|
|
|
|
20
|
6
|
|
|
6
|
|
36003
|
use String::Random; |
|
6
|
|
|
|
|
18042
|
|
|
6
|
|
|
|
|
290
|
|
21
|
6
|
|
|
6
|
|
4762
|
use Path::Class; |
|
6
|
|
|
|
|
205296
|
|
|
6
|
|
|
|
|
4131
|
|
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
sub register { |
24
|
5
|
|
|
5
|
1
|
330
|
my ($self, $app, $conf) = @_; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
# Plugin config |
27
|
5
|
|
50
|
|
|
25
|
$conf ||= {}; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# setting |
30
|
5
|
|
100
|
|
|
50
|
$self->parameter_name($conf->{parameter_name} || 'csrftoken'); |
31
|
5
|
|
100
|
|
|
120
|
$self->session_key($conf->{session_key} || 'csrftoken'); |
32
|
5
|
|
100
|
|
|
60
|
$self->token_length($conf->{token_length} || 32); |
33
|
5
|
|
100
|
|
|
51
|
$self->error_status($conf->{error_status} || 403); |
34
|
5
|
|
100
|
|
|
50
|
$self->error_content($conf->{error_content} || 'Forbidden'); |
35
|
5
|
|
100
|
|
|
52
|
$self->onetime($conf->{onetime} || 0); |
36
|
5
|
100
|
|
|
|
35
|
if ($conf->{error_template}) { |
37
|
1
|
|
|
|
|
33
|
my $file = $app->home->rel_file($conf->{error_template}); |
38
|
1
|
|
|
|
|
123
|
$self->error_template($file); |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# input check |
42
|
|
|
|
|
|
|
$app->hook(before_dispatch => sub { |
43
|
29
|
|
|
29
|
|
753730
|
my ($c) = @_; |
44
|
29
|
100
|
|
|
|
187
|
unless ($self->_validate_csrf($c)) { |
45
|
9
|
|
|
|
|
16
|
my $content; |
46
|
9
|
100
|
|
|
|
54
|
if ($self->error_template) { |
47
|
2
|
|
|
|
|
17
|
my $file = file($self->error_template); |
48
|
2
|
|
|
|
|
471
|
$content = $file->slurp; |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
else { |
51
|
7
|
|
|
|
|
61
|
$content = $self->{error_content}, |
52
|
|
|
|
|
|
|
} |
53
|
9
|
|
|
|
|
869
|
$c->render( |
54
|
|
|
|
|
|
|
status => $self->{error_status}, |
55
|
|
|
|
|
|
|
text => $content, |
56
|
|
|
|
|
|
|
); |
57
|
|
|
|
|
|
|
}; |
58
|
5
|
|
|
|
|
66
|
}); |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# output filter |
61
|
|
|
|
|
|
|
$app->hook(after_dispatch => sub { |
62
|
29
|
|
|
29
|
|
153954
|
my ($c) = @_; |
63
|
29
|
|
|
|
|
141
|
my $token = $self->_get_csrf_token($c); |
64
|
29
|
|
|
|
|
641
|
my $p_name = $self->parameter_name; |
65
|
29
|
|
|
|
|
196
|
my $body = $c->res->body; |
66
|
29
|
|
|
|
|
6266
|
$body =~ s{( |
67
|
29
|
|
|
|
|
328
|
$c->res->body($body); |
68
|
5
|
|
|
|
|
273
|
}); |
69
|
|
|
|
|
|
|
|
70
|
5
|
|
|
|
|
205
|
return $self; |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
sub _validate_csrf { |
74
|
29
|
|
|
29
|
|
65
|
my ($self, $c) = @_; |
75
|
|
|
|
|
|
|
|
76
|
29
|
|
|
|
|
176
|
my $p_name = $self->parameter_name; |
77
|
29
|
|
|
|
|
291
|
my $s_name = $self->session_key; |
78
|
29
|
|
|
|
|
239
|
my $request_token = $c->req->param($p_name); |
79
|
29
|
|
|
|
|
11937
|
my $session_token = $c->session($s_name); |
80
|
|
|
|
|
|
|
|
81
|
29
|
100
|
|
|
|
590
|
if ($c->req->method eq 'POST') { |
82
|
15
|
100
|
|
|
|
1233
|
return 0 unless $request_token; |
83
|
6
|
50
|
|
|
|
24
|
return 0 unless $session_token; |
84
|
6
|
50
|
|
|
|
28
|
return 0 unless $request_token eq $session_token; |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# onetime |
88
|
20
|
100
|
100
|
|
|
1189
|
if ($c->req->method eq 'POST' && $self->onetime) { |
89
|
2
|
|
|
|
|
134
|
$c->session($self->{session_key} => ''); |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
|
92
|
20
|
|
|
|
|
1648
|
return 1; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
sub _get_csrf_token { |
96
|
29
|
|
|
29
|
|
224
|
my ($self, $c) = @_; |
97
|
|
|
|
|
|
|
|
98
|
29
|
|
|
|
|
132
|
my $key = $self->session_key; |
99
|
29
|
|
|
|
|
395
|
my $token = $c->session($key); |
100
|
29
|
|
|
|
|
747
|
my $length = $self->token_length; |
101
|
29
|
100
|
|
|
|
247
|
return $token if $token; |
102
|
|
|
|
|
|
|
|
103
|
7
|
|
|
|
|
63
|
$token = String::Random::random_regex("[a-zA-Z0-9_]{$length}"); |
104
|
7
|
|
|
|
|
2880
|
$c->session($key => $token); |
105
|
7
|
|
|
|
|
194
|
return $token; |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
1; |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
__END__ |