View | Details | Raw Unified | Return to bug 16929
Collapse All | Expand All

(-)a/Koha/Token.pm (+82 lines)
Line 0 Link Here
1
package Koha::Token;
2
3
# Created as wrapper for CSRF tokens, but designed for more general use
4
5
use Modern::Perl;
6
7
use base qw(Class::Accessor);
8
9
use constant HMAC_SHA1_LENGTH => 20;
10
11
sub new {
12
    my ( $class ) = @_;
13
    return $class->SUPER::new();
14
}
15
16
sub generate {
17
    my ( $self, $params ) = @_;
18
    if( $params->{CSRF} ) {
19
        $self->{lasttoken} = _gen_csrf( $params );
20
    } else {
21
        $self->{lasttoken} = _gen_rand( $params );
22
    }
23
    return $self->{lasttoken};
24
}
25
26
sub check {
27
    my ( $self, $params ) = @_;
28
    if( $params->{CSRF} ) {
29
        return _chk_csrf( $params );
30
    }
31
    return;
32
}
33
34
#--- Internal routines
35
36
sub _gen_csrf {
37
    my ( $params ) = @_;
38
    return if !$params->{id} || !$params->{secret};
39
40
    require WWW::CSRF;
41
    my $token;
42
    local $SIG{ALRM} = sub { die "alarm\n"; };
43
    alarm 2;
44
    eval {
45
        $token = WWW::CSRF::generate_csrf_token(
46
            $params->{id}, $params->{secret},
47
        );
48
    };
49
    alarm 0;
50
    warn "L49 alarm" if $@ =~ /alarm/;
51
    if( !$token ) {
52
        my $mask = _gen_rand({ length => HMAC_SHA1_LENGTH });
53
        $token = WWW::CSRF::generate_csrf_token(
54
            $params->{id}, $params->{secret}, { Random => $mask },
55
        );
56
    }
57
    return $token;
58
}
59
60
sub _chk_csrf {
61
    my ( $params ) = @_;
62
    return if !$params->{id} || !$params->{secret} || !$params->{token};
63
64
    require WWW::CSRF;
65
    my $csrf_status = WWW::CSRF::check_csrf_token(
66
        $params->{id},
67
        $params->{secret},
68
        $params->{token},
69
    );
70
    return $csrf_status == WWW::CSRF::CSRF_OK();
71
}
72
73
sub _gen_rand {
74
    my ( $params ) = @_;
75
    my $length = $params->{length} || 1;
76
    $length = 1 unless $length > 0;
77
78
    require String::Random;
79
    return String::Random::random_string( '.' x $length );
80
}
81
82
1;
(-)a/opac/opac-memberentry.pl (-6 / +22 lines)
Lines 20-26 use Modern::Perl; Link Here
20
use CGI qw ( -utf8 );
20
use CGI qw ( -utf8 );
21
use Digest::MD5 qw( md5_base64 md5_hex );
21
use Digest::MD5 qw( md5_base64 md5_hex );
22
use String::Random qw( random_string );
22
use String::Random qw( random_string );
23
use WWW::CSRF qw(generate_csrf_token check_csrf_token CSRF_OK);
24
23
25
use C4::Auth;
24
use C4::Auth;
26
use C4::Output;
25
use C4::Output;
Lines 33-38 use C4::Scrubber; Link Here
33
use Email::Valid;
32
use Email::Valid;
34
use Koha::DateUtils;
33
use Koha::DateUtils;
35
use Koha::Patron::Images;
34
use Koha::Patron::Images;
35
use Koha::Token;
36
36
37
my $cgi = new CGI;
37
my $cgi = new CGI;
38
my $dbh = C4::Context->dbh;
38
my $dbh = C4::Context->dbh;
Lines 182-189 if ( $action eq 'create' ) { Link Here
182
elsif ( $action eq 'update' ) {
182
elsif ( $action eq 'update' ) {
183
183
184
    my $borrower = GetMember( borrowernumber => $borrowernumber );
184
    my $borrower = GetMember( borrowernumber => $borrowernumber );
185
    my $csrf_status = check_csrf_token($borrower->{userid}, md5_base64(C4::Context->config('pass')), scalar $cgi->param('csrf_token'));
185
    die "Wrong CSRF token"
186
    die "Wrong CSRF token" unless ($csrf_status == CSRF_OK);
186
        unless Koha::Token->new->check(
187
            id     => $borrower->{userid},
188
            secret => md5_base64( C4::Context->config('pass') ),
189
            token  => scalar $cgi->param('csrf_token'),
190
        );
187
191
188
    my %borrower = ParseCgiForBorrower($cgi);
192
    my %borrower = ParseCgiForBorrower($cgi);
189
193
Lines 197-203 elsif ( $action eq 'update' ) { Link Here
197
            empty_mandatory_fields => \@empty_mandatory_fields,
201
            empty_mandatory_fields => \@empty_mandatory_fields,
198
            invalid_form_fields    => $invalidformfields,
202
            invalid_form_fields    => $invalidformfields,
199
            borrower               => \%borrower,
203
            borrower               => \%borrower,
200
            csrf_token             => generate_csrf_token($borrower->{userid}, md5_base64(C4::Context->config('pass'))),
204
            csrf_token             => Koha::Token->new->generate({
205
                CSRF   => 1,
206
                id     => $borrower->{userid},
207
                secret => md5_base64( C4::Context->config('pass') ),
208
            }),
201
        );
209
        );
202
210
203
        $template->param( action => 'edit' );
211
        $template->param( action => 'edit' );
Lines 229-235 elsif ( $action eq 'update' ) { Link Here
229
                action => 'edit',
237
                action => 'edit',
230
                nochanges => 1,
238
                nochanges => 1,
231
                borrower => GetMember( borrowernumber => $borrowernumber ),
239
                borrower => GetMember( borrowernumber => $borrowernumber ),
232
                csrf_token => generate_csrf_token($borrower->{userid}, md5_base64(C4::Context->config('pass')))
240
                csrf_token => Koha::Token->new->generate({
241
                    CSRF   => 1,
242
                    id     => $borrower->{userid},
243
                    secret => md5_base64( C4::Context->config('pass') ),
244
                }),
233
            );
245
            );
234
        }
246
        }
235
    }
247
    }
Lines 249-255 elsif ( $action eq 'edit' ) { #Display logged in borrower's data Link Here
249
        borrower  => $borrower,
261
        borrower  => $borrower,
250
        guarantor => scalar Koha::Patrons->find($borrowernumber)->guarantor(),
262
        guarantor => scalar Koha::Patrons->find($borrowernumber)->guarantor(),
251
        hidden => GetHiddenFields( $mandatory, 'modification' ),
263
        hidden => GetHiddenFields( $mandatory, 'modification' ),
252
        csrf_token => generate_csrf_token($borrower->{userid}, md5_base64(C4::Context->config('pass')))
264
        csrf_token => Koha::Token->new->generate({
265
            CSRF   => 1,
266
            id     => $borrower->{userid},
267
            secret => md5_base64( C4::Context->config('pass') ),
268
        }),
253
    );
269
    );
254
270
255
    if (C4::Context->preference('OPACpatronimages')) {
271
    if (C4::Context->preference('OPACpatronimages')) {
(-)a/t/Token.t (-1 / +13 lines)
Line 0 Link Here
0
- 
1
use Koha::Token;
2
3
my $tokenizer = Koha::Token->new;
4
my $token = $tokenizer->generate({ length => 20 });
5
print "$token\n";
6
my $token = $tokenizer->generate({ CSRF => 1, id => 'ed', secret => 'x@#' });
7
print "$token\n";
8
my $r = $tokenizer->check({ CSRF => 1, id => 'ed', secret => 'x@#', token => $token });
9
print "r=$r\n";
10
substr($token, -2) = '12';
11
$r = $tokenizer->check({ CSRF => 1, id => 'ed', secret => 'x@#', token => $token });
12
print "r=$r\n";
13

Return to bug 16929