diff options
author | Miquel Lionel <lionel@les-miquelots.net> | 2021-07-02 12:09:24 +0100 |
---|---|---|
committer | Miquel Lionel <lionelmiquel@sfr.fr> | 2021-07-04 00:28:00 +0100 |
commit | 2137a587bb3a314ac13327d4cdb53f0ee8b9d970 (patch) | |
tree | cea28ad0f1b4782a5eea556f5346f712108d1c6e /gpigeon-template.cgi | |
parent | 23c6d4753895870224209a44624c1350e934f762 (diff) | |
download | gpigeon-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-x | gpigeon-template.cgi | 117 |
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} 🔗</th> + <th>$text_strings{theader_for} 📧</th> + <th>$text_strings{theader_deletion} 🗑</th> </tr> @created_links </table> |