Line 0
Link Here
|
|
|
1 |
package Koha::Token; |
2 |
|
3 |
# Created as wrapper for CSRF tokens, but designed for more general use |
4 |
|
5 |
# Copyright 2016 Rijksmuseum |
6 |
# |
7 |
# This file is part of Koha. |
8 |
# |
9 |
# Koha is free software; you can redistribute it and/or modify it under the |
10 |
# terms of the GNU General Public License as published by the Free Software |
11 |
# Foundation; either version 3 of the License, or (at your option) any later |
12 |
# version. |
13 |
# |
14 |
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY |
15 |
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR |
16 |
# A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
17 |
# |
18 |
# You should have received a copy of the GNU General Public License along |
19 |
# with Koha; if not, write to the Free Software Foundation, Inc., |
20 |
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
21 |
|
22 |
=head1 NAME |
23 |
|
24 |
Koha::Token - Tokenizer |
25 |
|
26 |
=head1 SYNOPSIS |
27 |
|
28 |
use Koha::Token; |
29 |
my $tokenizer = Koha::Token->new; |
30 |
my $token = $tokenizer->generate({ length => 20 }); |
31 |
|
32 |
# safely generate a CSRF token (nonblocking) |
33 |
my $csrf_token = $tokenizer->generate({ |
34 |
CSRF => 1, id => $id, secret => $secret, |
35 |
}); |
36 |
|
37 |
# or check a CSRF token |
38 |
my $result = $tokenizer->check({ |
39 |
CSRF => 1, id => $id, secret => $secret, token => $token, |
40 |
}); |
41 |
|
42 |
=head1 DESCRIPTION |
43 |
|
44 |
Designed for providing general tokens. |
45 |
Created due to the need for a nonblocking call to Bytes::Random::Secure |
46 |
when generating a CSRF token. |
47 |
|
48 |
=cut |
49 |
|
50 |
use Modern::Perl; |
51 |
use base qw(Class::Accessor); |
52 |
use constant HMAC_SHA1_LENGTH => 20; |
53 |
|
54 |
=head1 METHODS |
55 |
|
56 |
=head2 new |
57 |
|
58 |
Create object (via Class::Accessor). |
59 |
|
60 |
=cut |
61 |
|
62 |
sub new { |
63 |
my ( $class ) = @_; |
64 |
return $class->SUPER::new(); |
65 |
} |
66 |
|
67 |
=head2 generate |
68 |
|
69 |
my $token = $tokenizer->generate({ length => 20 }); |
70 |
my $csrf_token = $tokenizer->generate({ |
71 |
CSRF => 1, id => $id, secret => $secret, |
72 |
}); |
73 |
|
74 |
Generate several types of tokens. Now includes CSRF. |
75 |
Room for future extension. |
76 |
|
77 |
=cut |
78 |
|
79 |
sub generate { |
80 |
my ( $self, $params ) = @_; |
81 |
if( $params->{CSRF} ) { |
82 |
$self->{lasttoken} = _gen_csrf( $params ); |
83 |
} else { |
84 |
$self->{lasttoken} = _gen_rand( $params ); |
85 |
} |
86 |
return $self->{lasttoken}; |
87 |
} |
88 |
|
89 |
=head2 check |
90 |
|
91 |
my $result = $tokenizer->check({ |
92 |
CSRF => 1, id => $id, secret => $secret, token => $token, |
93 |
}); |
94 |
|
95 |
Check several types of tokens. Now includes CSRF. |
96 |
Room for future extension. |
97 |
|
98 |
=cut |
99 |
|
100 |
sub check { |
101 |
my ( $self, $params ) = @_; |
102 |
if( $params->{CSRF} ) { |
103 |
return _chk_csrf( $params ); |
104 |
} |
105 |
return; |
106 |
} |
107 |
|
108 |
# --- Internal routines --- |
109 |
|
110 |
sub _gen_csrf { |
111 |
|
112 |
# Since WWW::CSRF::generate_csrf_token does not use the NonBlocking |
113 |
# parameter of Bytes::Random::Secure, we are passing random bytes from |
114 |
# a non blocking source to WWW::CSRF via its Random parameter. |
115 |
|
116 |
my ( $params ) = @_; |
117 |
return if !$params->{id} || !$params->{secret}; |
118 |
|
119 |
require Bytes::Random::Secure; |
120 |
require WWW::CSRF; |
121 |
|
122 |
my $randomizer = Bytes::Random::Secure->new( NonBlocking => 1 ); |
123 |
# this is most fundamental: do not use /dev/random since it is |
124 |
# blocking, but use /dev/urandom ! |
125 |
my $random = $randomizer->bytes( HMAC_SHA1_LENGTH ); |
126 |
my $token = WWW::CSRF::generate_csrf_token( |
127 |
$params->{id}, $params->{secret}, { Random => $random }, |
128 |
); |
129 |
|
130 |
return $token; |
131 |
} |
132 |
|
133 |
sub _chk_csrf { |
134 |
my ( $params ) = @_; |
135 |
return if !$params->{id} || !$params->{secret} || !$params->{token}; |
136 |
|
137 |
require WWW::CSRF; |
138 |
my $csrf_status = WWW::CSRF::check_csrf_token( |
139 |
$params->{id}, |
140 |
$params->{secret}, |
141 |
$params->{token}, |
142 |
); |
143 |
return $csrf_status == WWW::CSRF::CSRF_OK(); |
144 |
} |
145 |
|
146 |
sub _gen_rand { |
147 |
my ( $params ) = @_; |
148 |
my $length = $params->{length} || 1; |
149 |
$length = 1 unless $length > 0; |
150 |
|
151 |
require String::Random; |
152 |
return String::Random::random_string( '.' x $length ); |
153 |
} |
154 |
|
155 |
=head1 AUTHOR |
156 |
|
157 |
Marcel de Rooy, Rijksmuseum Amsterdam, The Netherlands |
158 |
|
159 |
=cut |
160 |
|
161 |
1; |