X-Git-Url: https://git.sesse.net/?p=itkacl;a=blobdiff_plain;f=itkacl-web-1.0%2Finclude%2Fitkaclcommon.pm;h=cd86a3b5ddb6bee1eca2a819cb6b9a9f8411095f;hp=07ba68248c5d9d7a3da582fa0584c5bcccf13257;hb=fa3b6a893838409e3486d210fed263a8f7347a99;hpb=d1016ff35687bf55cff48a884c6fdcefe64e5d7d diff --git a/itkacl-web-1.0/include/itkaclcommon.pm b/itkacl-web-1.0/include/itkaclcommon.pm index 07ba682..cd86a3b 100644 --- a/itkacl-web-1.0/include/itkaclcommon.pm +++ b/itkacl-web-1.0/include/itkaclcommon.pm @@ -6,6 +6,7 @@ use DBI; use Apache::Session::Postgres; use Encode; use HTML::Entities; +use Digest::HMAC_SHA1; use locale; use utf8; @@ -17,6 +18,8 @@ our $cgi; our $dbh; our $last_modified = '$Date: 2011-11-19 11:08:01 $'; our %session; +our $csrf_token; +our $masked_csrf_token; sub init { $cgi = new CGI; @@ -26,6 +29,46 @@ sub init { $dbh->{pg_enable_utf8} = 1; $last_modified = '$Date: 2011-11-19 11:08:01 $'; %session = (); + + if (defined($ENV{'REMOTE_USER'})) { + $csrf_token = Digest::HMAC_SHA1::hmac_sha1($ENV{'REMOTE_USER'}, $itkaclcommon::hmac_key); + + # Mask the CSRF token to avoid the BREACH attack. + my @hmac_bytes = map { ord($_) } (split //, $csrf_token); + my @random_bytes = map { int(rand(256)) } (0..$#hmac_bytes); + my @masked_bytes = map { $hmac_bytes[$_] ^ $random_bytes[$_] } (0..$#hmac_bytes); + + my $random_bytes_string = join('', map { chr($_) } (@random_bytes)); + my $masked_bytes_string = join('', map { chr($_) } (@masked_bytes)); + $masked_csrf_token = unpack('H*', $random_bytes_string) . '_' . unpack("H*", $masked_bytes_string); + } else { + $csrf_token = ''; + $masked_csrf_token = ''; + } +} + +sub check_csrf_token { + if ($csrf_token eq '') { + # Not logged in, so always fine. + return 1; + } + + my $candidate_csrf_token = $cgi->param('csrftoken'); + if ($candidate_csrf_token !~ /^([0-9a-f]+)_([0-9a-f]+)$/) { + die "Invalid CSRF token!"; + } + + my ($random_bytes_string, $masked_bytes_string) = ($1, $2); + if (length($random_bytes_string) != length($masked_bytes_string)) { + die "Length mismatch in CSRF token!"; + } + + my @random_bytes = map { ord($_) } (split //, pack('H*', $random_bytes_string)); + my @masked_bytes = map { ord($_) } (split //, pack('H*', $masked_bytes_string)); + my @hmac_bytes = map { $masked_bytes[$_] ^ $random_bytes[$_] } (0..$#masked_bytes); + my $hmac_string = join('', map { chr($_) } (@hmac_bytes)); + + die "CSRF token mismatch!" if ($hmac_string ne $csrf_token); } sub print_header {