aboutsummaryrefslogtreecommitdiff
path: root/gpigeon-template.cgi
diff options
context:
space:
mode:
authorMiquel Lionel <lionel@les-miquelots.net>2021-07-02 12:09:24 +0100
committerMiquel Lionel <lionelmiquel@sfr.fr>2021-07-04 00:28:00 +0100
commit2137a587bb3a314ac13327d4cdb53f0ee8b9d970 (patch)
treecea28ad0f1b4782a5eea556f5346f712108d1c6e /gpigeon-template.cgi
parent23c6d4753895870224209a44624c1350e934f762 (diff)
downloadgpigeon-2137a587bb3a314ac13327d4cdb53f0ee8b9d970.tar.gz
gpigeon-2137a587bb3a314ac13327d4cdb53f0ee8b9d970.zip
Better way to validate cookies and others improvs
- Added GPLv3+ short header in source files (genpass.pl, gpigeon-template.cgi and link-template-tmpl.cgi). - Added some security headers in the example nginx configuration file, and renamed the NGINXCONFDIR variable in the Makefile to SITESENABLED, it makes a bit more sense. - Hastily drawed a more fitting .ico/mascot for the project - Tweaked the styles.css to be somewhat more responsive. Some tags in index.html and gpigeon-template.cgi and link-template-tmpl.cgi have now an id for styling. - Fixed and improved cookie validation. While working on the multi-user alternative, I noticed that the ValidCookies() function was flimsy, I was used eq... I learned about 'cmp' and throwed some UA and IP address match to make it a bit more robust. - Improved the genpass.pl script, if you want a argon2id of your password, you can now launch it in interactive mode with the '-i' arg. It'll fill the ARGON2ID_HASH variable in the existing config.mk with the hash of the provided password - Fixed inconsistencies in the Makefile. I was overwriting changes with sed for no good reason instead of using the -i switch ! I also moved the mail address, mail sender, and gpg id checks to the top of the file since they are the most important. Also, the 0xlong is not needed in config.def.mk anymore, we extract via the mail address.
Diffstat (limited to 'gpigeon-template.cgi')
-rwxr-xr-xgpigeon-template.cgi117
1 files changed, 75 insertions, 42 deletions
diff --git a/gpigeon-template.cgi b/gpigeon-template.cgi
index 6ee79d9..e8c5036 100755
--- a/gpigeon-template.cgi
+++ b/gpigeon-template.cgi
@@ -1,7 +1,24 @@
#! /usr/bin/perl -T
+# gpigeon.cgi: generate links for someone to send you GPG encrypted messages via a one time form.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+# Copyright (c) 2020-2021, Miquel Lionel <lionel@les-miquelots.net>
use warnings;
use strict;
+use File::Path qw(mkpath rmtree);
use Crypt::Argon2 qw(argon2id_verify);
use Email::Valid;
use String::Random;
@@ -9,60 +26,76 @@ use CGI qw(param);
use CGI::Cookie;
use CGI::Carp qw(fatalsToBrowser);
+my $uagent = $ENV{HTTP_USER_AGENT};
+my $rIP = $ENV{REMOTE_ADDR};
+my $hostname = $ENV{'SERVER_NAME'};
+
sub ValidCookie {
my $client_login_cookie = shift;
my $dir = shift;
+ my $filename = $client_login_cookie->value;
+ my $login_cookiefile = "$dir/$filename.txt";
+
if (not defined $client_login_cookie){
return;
}
- my $cookie_line = undef;
- my $filename = $client_login_cookie->value;
if ($filename =~ /^([\w]+)$/){
$filename = $1;
}
else{
return;
}
- my $login_cookiefile = "$dir/$filename.txt";
+
if (-e $login_cookiefile){
open my $in, '<', $login_cookiefile or die "can't read file: $!";
- my $cookie_line = readline $in;
+ $rip_line = readline $in;
+ $ua_line = readline $in;
+ $cookie_line = readline $in;
close $in;
+ chomp ($rip_line, $ua_line);
if (not defined $cookie_line){
return;
}
+ my %magic_cookie = CGI::Cookie->parse($cookie_line) or die "$!";
+ my $magic_cookie_val = $magic_cookie{'id'}->value;
+
+ my $rip_match = $rip_line cmp $rIP;
+ my $ua_match = $ua_line cmp $uagent;
+ my $magic_match = $magic_cookie_val cmp $client_login_cookie->value;
+
+ if ($rip_match == 0 and $ua_match == 0 and $magic_match == 0){
+ return 1;
+ }
}
else{
return;
}
-
- if ($client_login_cookie == $cookie_line){
- return 1;
- }
return;
-
}
sub LoginCookieGen {
my $id_cookie = shift;
my $dir = shift;
- my $str_rand_obj = String::Random->new;
- my $val = $str_rand_obj->randregex('\w{64}');
- my $cookiefile = "$dir/$val.txt";
- my $new_login_cookie = CGI::Cookie->new(
- -name => 'id',
- -value => $val,
- -expires => '+1y',
- '-max-age' => '+1y',
- -domain => ".$ENV{'SERVER_NAME'}",
- -path => '/',
- -secure => 1,
- -httponly => 1,
- -samesite => 'Strict',
- ) or die "Can't create cookie: $!";
if (not defined $id_cookie){
+ if (not -d "$dir"){
+ mkpath("$dir") or die "$!";
+ }
+ my $str_rand_obj = String::Random->new;
+ my $val = $str_rand_obj->randregex('\w{64}');
+ my $cookiefile = "$dir/$val.txt";
+ my $new_login_cookie = CGI::Cookie->new(
+ -name => 'id',
+ -value => $val,
+ -expires => '+1y',
+ '-max-age' => '+1y',
+ -domain => ".$hostname",
+ -path => '/',
+ -secure => 1,
+ -httponly => 1,
+ -samesite => 'Strict',
+ ) or die "Can't create cookie: $!";
open my $out, '>', $cookiefile or die "Can't write to $cookiefile: $!";
- print $out "$new_login_cookie";
+ print $out "$rIP\n$uagent\n$new_login_cookie";
close $out;
print "Set-Cookie: $new_login_cookie\n";
}
@@ -102,7 +135,6 @@ my ($linkgen_notif, $link_asker, $mailisok_notif, $deletion_notif,
my @created_links = ();
delete @ENV{qw(IFS PATH CDPATH BASH_ENV)};
$ENV{'PATH'} = '/usr/bin';
-my $hostname = $ENV{'SERVER_NAME'};
my $argon2id_hash = q{argon2id_hash_goes_here};
my $cookies_dir = q{cookies_dir_goes_here};
@@ -127,7 +159,7 @@ my %text_strings = (
link_del_failed => 'Deletion failed and here is why : ',
mailto_body => 'Your link is ',
mailto_subject => 'Link to your one time GPG messaging form',
- notif_login_failure => 'Cannot login. Check if your username and password match.'
+ notif_login_failure => 'Cannot login. Check if your username and password match.',
refresh_btn_text => 'Refresh',
type_msg_below => 'Type your message below',
theader_link => 'Link',
@@ -146,12 +178,13 @@ if (not defined $id_cookie){
$hidden_pwfield = qq{<input type="hidden" name="password" value="$pw">};
$refresh_form = qq{<form method="POST">
$hidden_pwfield
- <input type="submit" value="$text_strings{refresh_btn_text}">
+ <input id="refreshbtn" type="submit" value="$text_strings{refresh_btn_text}">
</form>};
}
else{
+ $hidden_pwfield = '<!-- undef -->';
$refresh_form = qq{<form method="GET">
- <input type="submit" value="$text_strings{refresh_btn_text}">
+ <input id="refreshbtn" type="submit" value="$text_strings{refresh_btn_text}">
</form>};
$idval = $id_cookie->value;
@@ -188,10 +221,10 @@ if (ValidCookie($id_cookie, $cookies_dir) or argon2id_verify($argon2id_hash,$pw)
my $pending_deletion = $cgi_query_get->param('supprlien');
my $linkfile_fn = "./l/$pending_deletion";
if (unlink UntaintCGIFilename($linkfile_fn)){
- $deletion_notif = qq{<span style="color:green">$text_strings{link_del_ok}</span>};
+ $deletion_notif = qq{<span id="success">$text_strings{link_del_ok}</span>};
}
else {
- $deletion_notif = qq{<span style="color:red">$text_strings{link_del_failed} $linkfile_fn : $!</span>};
+ $deletion_notif = qq{<span id="failure">$text_strings{link_del_failed} $linkfile_fn : $!</span>};
}
}
@@ -200,7 +233,7 @@ if (ValidCookie($id_cookie, $cookies_dir) or argon2id_verify($argon2id_hash,$pw)
while (readdir $link_dir_handle) {
if ($_ ne '.' and $_ ne '..'){
unlink UntaintCGIFilename("./l/$_") or die "$!";
- $deletion_notif = qq{<span style="color:green">$text_strings{link_del_ok}</span>};
+ $deletion_notif = qq{<span id="success">$text_strings{link_del_ok}</span>};
}
}
closedir $link_dir_handle;
@@ -210,7 +243,7 @@ if (ValidCookie($id_cookie, $cookies_dir) or argon2id_verify($argon2id_hash,$pw)
$link_asker = scalar $cgi_query_get->param('mail');
if ( Email::Valid->address($link_asker) ){
- $mailisok_notif = qq{<span style="color:green">$text_strings{addr} $link_asker $text_strings{addr_ok}</span>};
+ $mailisok_notif = qq{<span id="success">$text_strings{addr} $link_asker $text_strings{addr_ok}</span>};
my $escaped_link_asker = EscapeArobase($link_asker);
my $str_rand_obj = String::Random->new;
my $generated_form_filename = $str_rand_obj->randregex('\w{64}') . '.cgi';
@@ -230,10 +263,10 @@ if (ValidCookie($id_cookie, $cookies_dir) or argon2id_verify($argon2id_hash,$pw)
close $in or die;
chmod(0755,$link_path) or die;
close $out or die;
- $linkgen_notif = qq{<span style="color:green">$text_strings{link_ok_for} $link_asker: </span><br><a href="$href">$href</a>};
+ $linkgen_notif = qq{<span id="success">$text_strings{link_ok_for} $link_asker: </span><br><a href="$href">$href</a>};
}
else{
- $mailisok_notif = qq{<span style="color:red">$text_strings{addr} $link_asker $text_strings{addr_nok}.</span>};
+ $mailisok_notif = qq{<span id="failure">$text_strings{addr} $link_asker $text_strings{addr_nok}.</span>};
}
}
@@ -249,7 +282,7 @@ if (ValidCookie($id_cookie, $cookies_dir) or argon2id_verify($argon2id_hash,$pw)
}
close $linkfile_handle;
- if (Email::Valid->address($link_asker){
+ if (Email::Valid->address($link_asker)){
push @created_links,
qq{<tr>
<td><a href="/cgi-bin/l/$linkfile_fn">$text_strings{here}</a></td>
@@ -258,7 +291,7 @@ if (ValidCookie($id_cookie, $cookies_dir) or argon2id_verify($argon2id_hash,$pw)
<form method="POST">
$hidden_pwfield
<input type="hidden" name="supprlien" value="$linkfile_fn">
- <input type="submit" value="$text_strings{delete_link_btn_text}">
+ <input id="deletelinkbtn" type="submit" value="$text_strings{delete_link_btn_text}">
</form>
</td>
</tr>};
@@ -286,7 +319,7 @@ if (ValidCookie($id_cookie, $cookies_dir) or argon2id_verify($argon2id_hash,$pw)
<p>$text_strings{web_greet_msg}</p>
<form method="GET">
<input type="hidden" name="logout" value="1">
- <input type="submit" value="$text_strings{logout_btn_text}">
+ <input id="logoutbtn" type="submit" value="$text_strings{logout_btn_text}">
</form>
$refresh_form
<hr>
@@ -295,7 +328,7 @@ if (ValidCookie($id_cookie, $cookies_dir) or argon2id_verify($argon2id_hash,$pw)
$hidden_pwfield
$text_strings{link_asker_field_label}<br>
<input tabindex="1" type="text" name="mail">
- <input tabindex="2" type="submit" value="$text_strings{create_link_btn}">
+ <input id="genlinkbtn" tabindex="2" type="submit" value="$text_strings{create_link_btn}">
</form>},
NotifIfDefined($mailisok_notif),
'<br>',
@@ -304,14 +337,14 @@ if (ValidCookie($id_cookie, $cookies_dir) or argon2id_verify($argon2id_hash,$pw)
<form method="POST">
$hidden_pwfield
<input type="hidden" name="supprtout">
- <input type="submit" value="$text_strings{delete_links_btn_text}">
+ <input id="deleteallbtn" type="submit" value="$text_strings{delete_links_btn_text}">
</form>},
NotifIfDefined($deletion_notif),
qq{<table>
<tr>
- <th>$text_strings{theader_link}</th>
- <th>$text_strings{theader_for}</th>
- <th>$text_strings{theader_deletion}</th>
+ <th>$text_strings{theader_link} &#128279;</th>
+ <th>$text_strings{theader_for} &#128231;</th>
+ <th>$text_strings{theader_deletion} &#128465;</th>
</tr>
@created_links
</table>