Skip to content
Permalink
Browse files Browse the repository at this point in the history
This adds CSRF protection to the profiles.
  • Loading branch information
justingit committed Sep 20, 2021
1 parent 626af66 commit d4d3d86
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 44 deletions.
71 changes: 47 additions & 24 deletions dada/DADA/App.pm
Expand Up @@ -15086,14 +15086,11 @@ sub profile_login {
}
}

if ( $DADA::Config::PROFILE_OPTIONS->{enabled} != 1) {
if ( $DADA::Config::PROFILE_OPTIONS->{enabled} != 1) {
return $self->default();

}

if ( $DADA::Config::PROFILE_OPTIONS->{enabled} != 1 ) {
return $self->default();
}

require DADA::Profile;
###
my $all_errors = [];
Expand All @@ -15109,7 +15106,6 @@ sub profile_login {
my $prof_sess = DADA::Profile::Session->new;

if ( $q->param('process') != 1 ) {

if ( $prof_sess->is_logged_in( { -cgi_obj => $q } )
&& $q->param('logged_out') != 1 )
{
Expand All @@ -15118,13 +15114,20 @@ sub profile_login {
-url => $DADA::Config::PROGRAM_URL . '/profile/' );
}
else {
my $scrn = '';
my $scrn = '';
my $using_captcha = 0;

if ( $DADA::Config::PROFILE_OPTIONS->{enable_captcha} == 1 ) {
$using_captcha = can_use_Google_reCAPTCHA();
}

my $auth_state;
if ( $DADA::Config::DISABLE_OUTSIDE_LOGINS == 1 ) {
require DADA::Security::SimpleAuthStringState;
my $sast = DADA::Security::SimpleAuthStringState->new;
$auth_state = $sast->make_state;
}

$scrn = DADA::Template::Widgets::wrap_screen(
{
-screen => 'profile_login.tmpl',
Expand Down Expand Up @@ -15167,6 +15170,9 @@ sub profile_login {
removal => scalar $q->param('removal') || '',
WHOLE_URL => $whole_url,

auth_state => $auth_state,


# This should probably be deprecated, as I'm handling this in
# DADA::Template::Widgets, now
%{ DADA::Profile::feature_enabled() }
Expand All @@ -15180,19 +15186,20 @@ sub profile_login {
else {
my ( $status, $errors ) = $prof_sess->validate_profile_login(
{
-email => xss_filter( scalar $q->param('login_email') ),
-password => xss_filter( scalar $q->param('login_password') ),
-email => xss_filter( scalar $q->param('login_email') ),
-password => xss_filter( scalar $q->param('login_password') ),
-auth_state => xss_filter( scalar $q->param('auth_state') ),

},
);

if ( $status == 1 ) {
my $cookie = $prof_sess->login(
{
-email => xss_filter( scalar $q->param('login_email') ),
-password =>
xss_filter( scalar $q->param('login_password') ),
},
-email => xss_filter( scalar $q->param('login_email') ),
-password => xss_filter( scalar $q->param('login_password') ),
-skip_validation => 1,
},
);

#DEV: encoding?
Expand Down Expand Up @@ -15397,18 +15404,31 @@ sub profile {
if ( $DADA::Config::PROFILE_OPTIONS->{enabled} != 1) {
return $self->default();
}




require DADA::Profile::Session;
my $prof_sess = DADA::Profile::Session->new;

if ( $prof_sess->is_logged_in( { -cgi_obj => $q } ) ) {
my $email = $prof_sess->get( { -cgi_obj => $q } );
if ($prof_sess->is_logged_in( { -cgi_obj => $q } ) ) {

if(length($q->param('process')) > 0) {

if($prof_sess->check_csrf($q) == 0){
$prof_sess->logout;
$q->param('flavor', 'profile_login');
return $self->profile_login();
}
}

my $prof_data = $prof_sess->get( { -cgi_obj => $q } );

require DADA::Profile::Fields;
require DADA::Profile;

my $prof = DADA::Profile->new( { -email => $email } );
my $dpf = DADA::Profile::Fields->new( { -email => $email } );
my $prof = DADA::Profile->new( { -email => $prof_data->{email} } );
my $dpf = DADA::Profile::Fields->new( { -email => $prof_data->{email} } );
my $subscriber_fields =
$dpf->{manager}->fields( { -show_hidden_fields => 0, } );
my $field_attr = $dpf->{manager}->get_all_field_attributes;
Expand Down Expand Up @@ -15614,7 +15634,7 @@ sub profile {
my $dps = DADA::Profile::Settings->new({-list => $list});
my $r = $dps->save(
{
-email => $email,
-email => $prof_data->{email},
-setting => 'delivery_prefs',
-value => $delivery_prefs,
}
Expand Down Expand Up @@ -15682,7 +15702,7 @@ sub profile {
my $dasu = DADA::App::Subscriptions::Unsub->new(
{ -list => $i->{list} } );
my $unsub_link = $dasu->unsub_link(
{ -email => $email, -mid => '00000000000000' } );
{ -email => $prof_data->{email}, -mid => '00000000000000' } );

my $digest_timeframe =
formatted_runtime( $ls->param('digest_schedule') );
Expand All @@ -15695,7 +15715,7 @@ sub profile {
);
my $s = $dps->fetch(
{
-email => $email,
-email => $prof_data->{email},
}
);
my $delivery_prefs = $s->{delivery_prefs} || 'individual';
Expand All @@ -15722,7 +15742,8 @@ sub profile {
-vars => {
errors => scalar $q->param('errors')
|| 0,
'profile.email' => $email,
'profile.email' => $prof_data->{email},
csrf_token => $prof_data->{token},
subscriber_fields => $fields,
subscriptions => $filled,
has_subscriptions => $has_subscriptions,
Expand All @@ -15749,7 +15770,7 @@ sub profile {
$DADA::Config::PROFILE_OPTIONS->{gravatar_options}
->{enable_gravators},
gravatar_img_url =>
gravatar_img_url( { -email => $email, } ),
gravatar_img_url( { -email => $prof_data->{email}, } ),
protected_directories => $protected_directories,
WHOLE_URL => $whole_url,
%{ DADA::Profile::feature_enabled() },
Expand Down Expand Up @@ -16014,8 +16035,10 @@ sub profile_update_email {
my $prof_sess = DADA::Profile::Session->new;
my $cookie = $prof_sess->login(
{
-email => $profile_info->{'profile.update_email'},
-no_pass => 1,
-email => $profile_info->{'profile.update_email'},
-no_pass => 1,
-skip_validation => 1,

}
);

Expand Down
75 changes: 65 additions & 10 deletions dada/DADA/Profile/Session.pm
Expand Up @@ -90,13 +90,20 @@ sub _login_cookie {

require CGI::Session;

use DADA::Security::Password;

my $token = DADA::Security::Password::generate_rand_string(undef, 41);

my $session = new CGI::Session( $self->{dsn}, $q, $self->{dsn_args} );

$session->param( 'email', cased($args->{ -email }) );
$session->param( '_logged_in', 1 );
$session->param( 'email', cased($args->{ -email }) );
$session->param( '_logged_in', 1 );
$session->param( 'token', $token);


$session->expire( $DADA::Config::COOKIE_PARAMS{ -expires } );
$session->expire( '_logged_in', $DADA::Config::COOKIE_PARAMS{ -expires } );
$session->expire( $DADA::Config::COOKIE_PARAMS{ -expires } );
$session->expire( '_logged_in', $DADA::Config::COOKIE_PARAMS{ -expires } );
$session->expire( 'token', $DADA::Config::COOKIE_PARAMS{ -expires } );

$cookie = $q->cookie(
%{$DADA::Config::PROFILE_OPTIONS->{cookie_params}},
Expand All @@ -106,10 +113,6 @@ sub _login_cookie {
) : ()

);

# My proposal to address the situation is quit relying on flush() happen
# automatically, and recommend that people use an explicit flush()
# instead, which works reliably for everyone.
$session->flush();

return $cookie;
Expand All @@ -124,7 +127,16 @@ sub login {
require CGI;
my $q = new CGI;

my ( $status, $errors ) = $self->validate_profile_login($args);
my ($status, $errors);

if($args->{-skip_validation} == 0){
( $status, $errors ) = $self->validate_profile_login($args);
}
else {
$status = 1;
$errors = {};
}

if ( $status == 0 ) {
croak "login failed.";
}
Expand Down Expand Up @@ -182,6 +194,20 @@ sub validate_profile_login {
incorrect_pass => 0,
};


require DADA::Security::SimpleAuthStringState;
my $sast = DADA::Security::SimpleAuthStringState->new;
my $auth_state = $args->{-auth_state};

if ( $DADA::Config::DISABLE_OUTSIDE_LOGINS == 1 ) {
if ( $sast->check_state($auth_state) != 1 ) {
$status = 0;
$errors->{invalid_form} = 1;
}

}


require DADA::Profile;
my $prof = DADA::Profile->new(
{
Expand Down Expand Up @@ -217,6 +243,30 @@ sub validate_profile_login {

}

sub check_csrf {
my $self = shift;
my $q = shift;

my $s = CGI::Session->load( $self->{dsn}, $q, $self->{dsn_args} )
or croak 'failed to load session: ' . CGI::Session->errstr();

if ( $s->is_expired ) {
return 0;
}

if ( $s->is_empty ) {
return 0;
}

if($q->param('csrf_token') eq $s->param('token') ){
return 1;
}
else {
return 0;
}


}
sub is_logged_in {

my $self = shift;
Expand All @@ -230,6 +280,8 @@ sub is_logged_in {
$q = new CGI;

}


my $s = CGI::Session->load( $self->{dsn}, $q, $self->{dsn_args} )
or croak 'failed to load session: ' . CGI::Session->errstr();

Expand Down Expand Up @@ -272,7 +324,10 @@ sub get {
require CGI::Session;

my $session = new CGI::Session( $self->{dsn}, $q, $self->{dsn_args} );
return $session->param('email');
return {
email => $session->param('email'),
token => $session->param('token'),
};

}

Expand Down
10 changes: 5 additions & 5 deletions dada/DADA/Template/Widgets.pm
Expand Up @@ -1250,7 +1250,7 @@ sub profile_widget {


my $scr = '';
my $email = '';
my $prof_data = {};
my $is_logged_in = 0;
my $profiles_enabled = $DADA::Config::PROFILE_OPTIONS->{enabled};
if ( $DADA::Config::PROFILE_OPTIONS->{enabled} != 1)
Expand All @@ -1261,7 +1261,7 @@ sub profile_widget {
if(defined($prof_sess_obj)){
if ( $prof_sess_obj->is_logged_in( { -cgi_obj => $q } ) ) {
$is_logged_in = 1;
$email = $prof_sess_obj->get( { -cgi_obj => $q } );
$prof_data = $prof_sess_obj->get( { -cgi_obj => $q } );
}
}
else {
Expand All @@ -1273,7 +1273,7 @@ sub profile_widget {
my $prof_sess = DADA::Profile::Session->new;
if ( $prof_sess->is_logged_in( { -cgi_obj => $q } ) ) {
$is_logged_in = 1;
$email = $prof_sess->get( { -cgi_obj => $q } );
$prof_data = $prof_sess->get( { -cgi_obj => $q } );
}
}
}
Expand All @@ -1285,11 +1285,11 @@ sub profile_widget {
-vars => {
profiles_enabled => $profiles_enabled,
is_logged_in => $is_logged_in,
'profile.email' => $email,
'profile.email' => $prof_data->{email},
gravators_enabled => $DADA::Config::PROFILE_OPTIONS->{gravatar_options}->{enable_gravators},
gravatar_img_url => gravatar_img_url(
{
-email => $email,
-email => $prof_data->{email},
-size => 45,
}
),
Expand Down
17 changes: 12 additions & 5 deletions dada/templates/profile_home.tmpl
Expand Up @@ -254,10 +254,11 @@
<legend>Delivery Preferences:</legend>
<form action="<!-- tmpl_var S_PROGRAM_URL -->" method="post">

<input type="hidden" name="flavor" value="profile" >
<input type="hidden" name="process" value="profile_delivery_preferences" >
<input type="hidden" name="list" value="<!-- tmpl_var list_settings.list -->" >

<input type="hidden" name="flavor" value="profile" >
<input type="hidden" name="process" value="profile_delivery_preferences" >
<input type="hidden" name="list" value="<!-- tmpl_var list_settings.list -->" >
<input type="hidden" name="csrf_token" value="<!-- tmpl_var csrf_token -->">

<div class="row">
<div class="small-10 medium-5 large-4 columns">

Expand Down Expand Up @@ -346,8 +347,10 @@
<div class="content-box section-box">

<form action="<!-- tmpl_var PROGRAM_URL -->" method="post" accept-charset="<!-- tmpl_var HTML_CHARSET -->" id="membership_profile_fields">
<input type="hidden" name="flavor" value="profile" >
<input type="hidden" name="flavor" value="profile" >
<input type="hidden" name="process" value="edit_subscriber_fields" >
<input type="hidden" name="csrf_token" value="<!-- tmpl_var csrf_token -->">

<!-- tmpl_loop subscriber_fields -->

<div class="row">
Expand Down Expand Up @@ -425,6 +428,8 @@
<form action="<!-- tmpl_var PROGRAM_URL -->" method="post" accept-charset="<!-- tmpl_var HTML_CHARSET -->">
<input type="hidden" name="flavor" value="profile" >
<input type="hidden" name="process" value="update_email" >
<input type="hidden" name="csrf_token" value="<!-- tmpl_var csrf_token -->">

<!-- tmpl_if errors_update_email -->
<!-- tmpl_if error_invalid_email -->
<p class="error">
Expand Down Expand Up @@ -471,6 +476,7 @@
<form action="<!-- tmpl_var PROGRAM_URL -->" method="post" accept-charset="<!-- tmpl_var HTML_CHARSET -->">
<input type="hidden" name="flavor" value="profile" >
<input type="hidden" name="process" value="change_password" >
<input type="hidden" name="csrf_token" value="<!-- tmpl_var csrf_token -->">

<!-- tmpl_if errors_change_password -->
<p class="error">
Expand Down Expand Up @@ -513,6 +519,7 @@
<form action="<!-- tmpl_var PROGRAM_URL -->" method="post" accept-charset="<!-- tmpl_var HTML_CHARSET -->">
<input type="hidden" name="flavor" value="profile" >
<input type="hidden" name="process" value="delete_profile" >
<input type="hidden" name="csrf_token" value="<!-- tmpl_var csrf_token -->">

<div class="alert-box info radius">
Deleting your profile will remove all your profile information, but
Expand Down

0 comments on commit d4d3d86

Please sign in to comment.