RSS Git Download  Clone
Raw Blame History
package LIMS::Local::CSRF;

=begin
prevents cross-site request forgery and browser reload button (except following
a redirect); see cgiapp mailing list "CAP::Security::CSRF -- useful?"
=cut

use strict;
use warnings;

use base 'Exporter';

use LIMS::Local::Utils;

use vars qw($VERSION @EXPORT);
@EXPORT = qw(csrf_insert_token csrf_check_token);
$VERSION = '1.0';

sub csrf_insert_token {
    my $self = shift;
    my $token_name = shift || 'csrf_token';
    
    # reuse the token that was submitted with the form; don't generate a new one
    return if $self->query->param('csrf_token');

    # generate a new token (not intended to be highly secure, or use random seed):
    my $str = $self->session->param('_SESSION_ID') . time;
    my $token = LIMS::Local::Utils::sha1_digest($str);
    
    $self->tt_params( csrf_token => $token );
    $self->session->param( $token_name => $token );
}

# returns true if POST'ed && query param token matches session token
# can set explicit token name, or accept default:
sub csrf_check_token {
    my $self = shift;
    my $token_name = shift || 'csrf_token';
    
    my $session_token = $self->session->param($token_name); # warn $session_token;
    my $query_token   = $self->query->param('csrf_token'); # warn $query_token;
    
    { # ensure the form was POST'ed and session token == query token:
        no warnings 'uninitialized'; # eg empty $session_token
        return 0 unless $self->query->request_method eq 'POST'
            && $session_token eq $query_token;
    }
    
    $self->session->clear($token_name);
    return 1;
}

1;