aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Benkard <matthias.benkard@egym.de>2015-04-14 08:39:27 +0000
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2015-04-23 21:55:02 +0200
commitba65cc50b7b468f0738398312a468ea413727bdc (patch)
tree19749bd8803a2f718013cf8901c04ea658f98731
parentfaea34e5c94922645b337bdeb5db32871eb1fde9 (diff)
QT-1900 Add a CSRF token to the OIDC login flow.
This improves security by generating a CSRF token, passing it to the OIDC IdP, and validating it afterwards. The token is stored in a cookie reverse-encrypted with MulkyID's private key.
-rw-r--r--build.PL4
-rwxr-xr-xwww/authenticate.pl28
-rw-r--r--www/common.pl27
-rwxr-xr-xwww/logged_in_p.pl1
-rwxr-xr-xwww/login.pl8
5 files changed, 54 insertions, 14 deletions
diff --git a/build.PL b/build.PL
index 6e008d3..7c320e2 100644
--- a/build.PL
+++ b/build.PL
@@ -29,7 +29,9 @@ my $build = Net::MulkyID::Builder->new
"OIDC::Lite::Client::WebServer" => 0,
"OIDC::Lite::Model::IDToken" => 0,
"LWP::UserAgent" => 0,
- "HTTP::Request" => 0
+ "HTTP::Request" => 0,
+ "Math::Random::ISAAC::XS" => 0,
+ "Bytes::Random::Secure" => 0
},
build_requires => {
"LWP::Simple" => 0,
diff --git a/www/authenticate.pl b/www/authenticate.pl
index 3c865ac..b2bd43f 100755
--- a/www/authenticate.pl
+++ b/www/authenticate.pl
@@ -13,6 +13,8 @@ use CGI::Fast;
use OIDC::Lite;
use OIDC::Lite::Client::WebServer;
+use Bytes::Random::Secure qw(random_bytes_base64);
+
do "common.pl";
while (my $cgi = new CGI::Fast) {
@@ -35,18 +37,20 @@ while (my $cgi = new CGI::Fast) {
authorize_uri => 'https://accounts.google.com/o/oauth2/auth',
access_token_uri => 'https://accounts.google.com/o/oauth2/token'
);
- # FIXME: Make `state` a unique, random session token! (Maybe a
- # signed, timestamped web token, so stateless?)
- print $cgi->redirect($oidc_client->uri_to_redirect(
- redirect_uri => reluri($cgi, 'login.pl'),
- scope => 'openid email',
- state => '',
- extra => {
- access_type => 'online',
- login_hint => $claimed_email,
- response_type => 'code'
- }
- ));
+ my $csrf_token = random_bytes_base64(32); #256 bits
+ my $csrf_token_cookie = make_cookie('mulkyid_csrf_token', $csrf_token);
+ print $cgi->redirect(
+ -cookie => $csrf_token_cookie,
+ -url => $oidc_client->uri_to_redirect(
+ redirect_uri => reluri($cgi, 'login.pl'),
+ scope => 'openid email',
+ state => $csrf_token,
+ extra => {
+ access_type => 'online',
+ login_hint => $claimed_email,
+ response_type => 'code'
+ })
+ );
}
default {
die "Invalid auth_type! " . $::MULKONF->{auth_type};
diff --git a/www/common.pl b/www/common.pl
index 63b8d0f..eb965f3 100644
--- a/www/common.pl
+++ b/www/common.pl
@@ -5,9 +5,12 @@ use common::sense;
#use Modern::Perl 2011;
use Modern::Perl;
+use File::Slurp;
use Mail::ExpandAliases;
use URI;
use MIME::Base64 qw(encode_base64 decode_base64);
+use Crypt::OpenSSL::RSA;
+use CGI::Cookie;
sub load_config() {
$::MULKONF = { };
@@ -54,3 +57,27 @@ sub encode_base64_url($) {
$s =~ s/\n//g;
return $s;
}
+
+sub acquire_private_key() {
+ my $key = Crypt::OpenSSL::RSA->new_private_key(scalar read_file($::MULKONF->{pemfile}));
+ $key->use_pkcs1_padding();
+ $key->use_sha256_hash();
+ return $key;
+}
+
+sub make_cookie($$) {
+ my ($name, $value) = @_;
+ my $key = acquire_private_key;
+ my $reverse_encrypted_value = $key->private_encrypt($value);
+ my $cookie = CGI::Cookie->new(-name => $name, -value =>encode_base64_url($reverse_encrypted_value));
+}
+
+sub read_cookie($$) {
+ my ($cgi, $name) = @_;
+ my $cookie = $cgi->cookie($name);
+ return unless ($cookie);
+ my $key = acquire_private_key;
+ my $value = $key->public_decrypt(decode_base64_url($cookie));
+ warn "cookie `$name` was forged!" unless $value;
+ return $value;
+}
diff --git a/www/logged_in_p.pl b/www/logged_in_p.pl
index b076618..5752959 100755
--- a/www/logged_in_p.pl
+++ b/www/logged_in_p.pl
@@ -52,4 +52,3 @@ while (my $cgi = new CGI::Fast) {
say encode_json({logged_in_p => 0});
}
}
-
diff --git a/www/login.pl b/www/login.pl
index fe1729d..5d27cbe 100755
--- a/www/login.pl
+++ b/www/login.pl
@@ -79,6 +79,14 @@ while (my $cgi = new CGI::Fast) {
}
when ('google') {
my $code = $cgi->param('code') or die "Authorization code is missing.";
+
+ # Validate CSRF token.
+ my $oauth_state = $cgi->param('state');
+ my $csrf_token = read_cookie($cgi, 'mulkyid_csrf_token');
+ unless ($csrf_token && $oauth_state && $csrf_token eq $oauth_state) {
+ die "CSRF token was forged!";
+ }
+
my $oidc_client = OIDC::Lite::Client::WebServer->new(
id => $::MULKONF->{'google_oauth2_client_id'},
secret => $::MULKONF->{'google_oauth2_client_secret'},