Experimental implementation of short-time password based on Apache HTTP Basic authentication feature. A source code pupblished.
Kobu.Com is good at research, development and prototyping. We can help you explore problem solving methods and study feasibility. Please contact.
Kobu.Com is building a database hosting service (comming soon now open, visit DBHosting). In doing so a kind of one-time password machanism is needed for account registration. I, Kobu.Com engineer, experimented two implementations of short-time passwords: one using an SQL table and the other using an HTTP basic authentication feature in Apache. I eventually adopted the former table-based method. But the latter HTTP authentiation method is found simple and useful. I would like to share a code (short bash shell script) for an HTTP basic authentication-based short-time password.
Source code: ticket.sh
I call this short-time password implementation ticket, meaning a limited-time permission to access a protected web page or application.
I first used the term one-time password but precisely this ticket implementation is short-time not one-time. A password owner can visit a protected page more than one time within a predetermined time. So I changed the term.
This is how it works:
This is one style of mailbox confirmation which I call pre-access check.
Another is post-access check. You occasionally sign up a new online service, such as shopping site. You enter your personal data such as email and postal addresses. At the end of registration process you are requested to check an email that will be sent to you by the service site. You click a URL in the email message which directs you to the shopping site again. Then the shopping site makes sure you are the legitimate owner of the email address.
The ticket service I describe in this page is different from the shopping site example in that it performs mailbox confirmation before you access the service. That's why I call the ticket style pre-access check while the shopping site style post-access check.
I experimented two pre-access mailbox confirmation methods: one using SQL and the other using an Apache feature.
I eventually dediced to use the table-based ticket because I need to store extra information about the requester other than username and password. I guess it is typical to use a database table to store information about a limited-use passwords. However I realized the basic authentication-based short-time passowrd is useful in some scenarios and providies the following advantages:
Let me show you the major part of the code that implements short-time password based on HTTP basic authentication with execution examples.
The examples below use dummy domains (example.com for visitor and example.net for server) while I actually tested with my domain names.
First a visitor sends an email to a domain which provides a service or page you want to use or view. The subject line should include a URL path (without domain) the visitor wants to access. The mail body can be anything or empty.
From: guest@example.com
To: ticket@example.net
Subject: /admin/auth_account.mdm
(No body text)
Here is a reply mail from a mail server of the receipient domain.
Hi, guest@example.com,
You have requested a ticket to visit /admin/auth_account.mdm on our site.
Click the following URL and type the username and password on a popup dialog:
https://example.net/admin/auth_account.mdm
Username: guestHexampleAcom
Password: ee6cac9a
For shortcut, you can just click the following URL:
https://geustHexampleAcom:ee6cac9a@example.net/admin/auth_account.mdm
If you are not expecting this email, please just ignore and discard it.
Thank you.
Ticket Office
At this moment the username and password are registered to the Apache web server running at the domain so that you can be admitted to access the target page.
At the server side, in order to receive a message to a specific email address and automatically reply with some message, you need to setup a mail handler script (or program). In postfix (mail server I used), enter the following line in /etc/aliases.
[/etc/aliases]
ticket: "|/usr/local/maliases/ticket.sh"
With this setting in place, when an email message arrives at 'ticket@example.net' then 'ticket.sh' script will be executed. The mail content is passed to the script through its standard input.
I used a different file instead of /etc/aliases and specified the file in 'alias_maps' line in /etc/main.cf. This allowed me to set the user running the script to an owner of the aliases file.
In addition to this, for debugging and logging purposes, I added redirection of stdout and stderr to a file.
-> ticket: "|/usr/local/maliases/ticket.sh >> /var/log/maliases/ticket.log 2>&1"
The received email message is passed to the mail handler through standard input in mbox format ('From' line followed by message content).
From guest@example.com Thu Jul 09 12:55:09 2020
Return-Path: <guest@example.com>
X-Original-To: ticket@example.net
Delivered-To: ticket@example.net
Received: ...
To: Live Embedder Ticket <ticket@example.net>
From: "Guest" <guest@example.com>
Subject: /admin/auth_account.mdm
Message-ID: <2e5e2435-0c74-fd8b-723f-583ed02674be@...>
Date: Thu, 9 Jul 2020 12:55:07 +0900
User-Agent: ...
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: 7bit
Content-Language: en-US
(one blank line)
The sender's email address is also passed through the SENDER environment variable. The only information I need from the mail content is the Subject line which contains a URL path the requester wants to access.
I used awk to get that:
# get path info from Subject header
path_info=$(cat | awk '/^Subject:/{print $2;}');
echo "path_info: $path_info"
|
v
path_info: /admin/auth_account.mdm
Actually username and password used short time as a ticket can be anything. An attacker is unable to forge a new ticket unless the attacker can put that username/password pair to the password file on the web server. I used, in my experiment, a sender's email address and a hex representation of a four-byte random number taken from /dev/urandom. I converted symbols in email address to uppercase letters so that I can embed it in a URL.
# sanitize email address so that it can be embedded in URL
username=$(echo $SENDER | tr "[:upper:]" "[:lower:]" | tr "@_.-" "HUAD")
echo "username: $username"
|
v
username: guest2HexampleAcom
# get a password from random number generator
password=$(xxd -p -l 4 /dev/urandom)
echo "password: $password"
|
v
password: ee6cac9a
I later used a perl module to handle more symbols than just four as shown above. For simplicity and readability an earlier shorter shell code is shown here.
Apache includes a command called htpasswd for creating a plain text password file used to store passwords for HTTP basic authentication.
When a web client accesses an area protected by basic authentication, Apache requests a username and password and checks them against authentication data under its management. An htpasswd-created password file is one of such data.
The following code adds the generated username/password pair to a password file:
# register username and password pair to Apache password file
[ -e $HTPWFILE ] || option_c='-c'
htpasswd $option_c -b $HTPWFILE $username $password
if [ $? -ne 0 ]; then
>&2 echo "Failed to write to $HTPWFILE: $username";
exit 0
fi
The HTPWFILE is a shell variable containing the path to the password file Apache will check when a URL in question is accessed. The '-c' option tells the htpasswd command whether the password file already exists or not.
The above script returns 0 as an exit code even if an error occurs. This avoids the mail server return an error mail to the sender.
Let me show you how the command works in the command line:
$ htpasswd -c -b /var/www/etc/htp_tickets guestHexampleAcom ee6cac9a
$ ls /var/www/etc/htp_tickets
-rw-rw-r-- 1 www-data www-data 53 Jul 9 12:55 htp_tickets
$ cat /var/www/etc/htp_tickets
guestHexampleAcom:$apr1$H8QE2KWc$99cegAYGAGohWy7O3aPMc/
A username and passowrd can be embedded in a URL in the following way:
full_url="https://$username:$password@example.net$path_info"
echo "full_url: $full_url"
|
v
full_url: https://guestHexampleAcom:ee6cac9a@example.net/admin/auth_account.mdm
The sendmail command (part of postfix package) is used to send a reply to the sender. The SENDMAIL variable contains the path to the sendmail command (/usr/sbin/sendmail in my case). The RECIPIENT variable has been set by postfix to the email address associated with the mail handler.
$SENDMAIL -F "Ticket Office" -f "$RECIPIENT" $SENDER <<REPLY_CONTENT
Subject: Re: $path_info
Hi, $SENDER,
You have requested a ticket to visit $path_info on our site.
Click the following URL and type the username and password on a popup dialog:
$plain_url
Username: $username
Password: $password
For shortcut, you can just click the following URL:
$full_url
If you are not expecting this email, please just ignore and discard it.
Thank you.
Ticket Office
REPLY_CONTENT
(A reply example has been show above.)
I decided a short-time password should expire in one hour. To implement this, I used at command to schedule deletion of password entry in the password file one hour after the mail handler script is executed:
# schedule deletion in one hour
at now + 1 hour <<AT_COMMAND
htpasswd -D $HTPWFILE $username
logger -s "[ticket.sh] ticket expired: path $path_info given to $SENDER"
AT_COMMAND
The -D option to the htpasswd command deletes an entry associated with a specified username. The logger command is used to log a message to syslog server. The '-s' option directs the logger command to output the message also to standard error.
The following is stdin and stdout output from the script captured in a file (domain names are changed):
[/var/log/maliases/ticket.log]
[ticket.sh] 20/07/09-12:55:09 SENDER = guest@example.com, RECIPIENT = ticket@example.net
path_info: /admin/auth_account.mdm
<13>Jul 9 12:55:09 www-data: [ticket.sh] ticket requested for /admin/auth_account.mdm to guest@example.com
username: guest2HexampleAcom
password: ee6cac9a
plain_url: https://example.net/admin/auth_account.mdm
full_url: https://guestHexampleAcom:ee6cac9a@example.net/admin/auth_account.mdm
Adding password for user guestHexampleAcom <- from htpasswd
warning: commands will be executed using /bin/sh <- from at command
job 19 at Thu Jul 9 13:55:00 2020
<13>Jul 9 12:55:09 www-data: [ticket.sh] ticket issued for /admin/auth_account.mdm to guest@examaple.com
The following is audit messages output to syslog.
[/var/log/messages]
Jul 9 12:55:09 ... www-data: [ticket.sh] ticket requested for /admin/auth_a
ccount.mdm to guest@example.com
Jul 9 12:55:09 ... www-data: [ticket.sh] ticket issued for /admin/auth_account.mdm to guest@example.com
Jul 9 13:55:00 ... www-data: [ticket.sh] ticket expired: path /admin/auth_account.mdm given to guest@example.com
You can see that the deletion script was executed one hour later the mail handler script ran.
To make sure, let me check the content of the password file.
$ ls -l /var/www/etc/htp_tickets
-rw-rw-r-- 1 www-data www-data 0 Jul 9 13:55
$ cat /var/www/etc/htp_tickets
$
You can see the password entry is not in the password file any more.
I explained how an experimental ticket (or limited-use password) implementation works.
A short-time password can be implemented by a simple mail handler script using Apache basic authentication mechanism. No programming is need for web page side; even a static page can be protected with a short-time password.
Kobu.Com is good at research, development and prototyping. We can help you explore problem solving methods and study feasibility. Please contact.
Presented by Kobu.Com
Written 2020-Jul-10
Updated 2020-Jul-31 fixed: $RECEIPIENT -> $RECIPIENT