From 0e14d7ac50d37a269026004b4afa2ecb340b8c85 Mon Sep 17 00:00:00 2001 From: hylom Date: Thu, 2 Feb 2017 22:45:31 +0900 Subject: [PATCH] Plugin::AntiCsrf: change token size, fix some methods to work, and ad hook in register method for auto validation --- src/newslash_web/lib/Newslash/Plugin/AntiCsrf.pm | 47 ++++++++++++++++++++---- src/newslash_web/lib/Newslash/Web.pm | 8 ++-- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/newslash_web/lib/Newslash/Plugin/AntiCsrf.pm b/src/newslash_web/lib/Newslash/Plugin/AntiCsrf.pm index 8b9fc9b1..f258d403 100644 --- a/src/newslash_web/lib/Newslash/Plugin/AntiCsrf.pm +++ b/src/newslash_web/lib/Newslash/Plugin/AntiCsrf.pm @@ -1,9 +1,12 @@ package Newslash::Plugin::AntiCsrf; use Mojo::Base 'Mojolicious::Plugin'; -use Mojo::Util qw(md5_sum dumper); +use Mojo::Util qw(md5_sum dumper b64_encode); +use Mojo::Log; use Crypt::OpenSSL::Random qw(random_seed random_bytes ); +use constant TOKEN_SIZE => 64; + # set random seed if (!random_seed(time())) { die 'random seed is not sufficient'; @@ -13,11 +16,13 @@ use constant KVS_KEY_PREFIX => 'csrf_tokens:'; sub _generate_token { my $self = shift; - my $randoms = random_bytes(128); + my $randoms = random_bytes(TOKEN_SIZE); if ($randoms) { - return b64_encode($randoms); + my $token = b64_encode($randoms); + chomp $token; + return $token; } - return undef; + return; } sub register { @@ -40,10 +45,36 @@ sub register { } $app->helper(anti_csrf => sub { state $anti_csrf = $self; }); + + $app->hook(before_render => sub { + my ($c, $args) = @_; + return if !defined $args->{json}; + my $check_id = $c->stash('csrf_check_id'); + return if !$check_id; + my $token = $self->get_token($check_id); + $args->{json}->{sec_token} = $token; + }); + + $app->hook(around_action => sub { + my ($next, $c, $action, $last) = @_; + return $next->() if $c->req->method ne 'POST'; + my $check_id = $c->stash('csrf_check_id'); + return $next->() if !$check_id; + + my $token = $c->req->json->{sec_token}; + if (!$token || !$self->validate_token($token, $check_id)) { + # validation error + $c->render(json => { error => 1, reason => "invalid_token", message => "AntiCsrf: validation error" }); + $c->rendered(400); + return; + } + return $next->(); + }); + } sub get_token { - my ($self, $resource_str) = shift; + my ($self, $resource_str) = @_; return if !$resource_str; my $token = $self->_generate_token; @@ -62,14 +93,14 @@ sub get_token { } sub validate_token { - my ($self, $token, $resource_str) = shift; + my ($self, $token, $resource_str) = @_; return if !$token; return if !$resource_str; my $kvs_key = KVS_KEY_PREFIX . $token; my $saved = $self->{kvs}->get($kvs_key); - if ($saved) { - return $saved eq $resource_str; + if ($saved && $saved eq $resource_str) { + return 1; } return; } diff --git a/src/newslash_web/lib/Newslash/Web.pm b/src/newslash_web/lib/Newslash/Web.pm index c27c3bb0..6c8b1022 100644 --- a/src/newslash_web/lib/Newslash/Web.pm +++ b/src/newslash_web/lib/Newslash/Web.pm @@ -122,11 +122,11 @@ sub startup { $api->post('/submission')->to('API::Submission#post'); $api->post('/story')->to('API::Story#post'); - $api->post('/moderation')->to('API::Moderation#post', seclev => 1); - $api->get('/moderation')->to('API::Moderation#get'); + $api->post('/moderation')->to('API::Moderation#post', seclev => 1, csrf_check_id => 'moderation'); + $api->get('/moderation')->to('API::Moderation#get',, csrf_check_id => 'moderation'); - $api->post('/metamoderation')->to('API::Metamoderation#post', seclev => 1); - $api->get('/metamoderation')->to('API::Metamoderation#get'); + $api->post('/metamoderation')->to('API::Metamoderation#post', seclev => 1, csrf_check_id => 'moderation'); + $api->get('/metamoderation')->to('API::Metamoderation#get', csrf_check_id => 'moderation'); } 1; -- 2.11.0