In my Department at the University Of Crete we have a set
of computers where you can login using ssh(1)
and access all your files,
write code, run programs, and in general perform all tasks you may need without
having to install linux on remove Windows from your computer. For
“Security” reasons, these computers are behind two stripped, chrooted “Gates”.
You cannot ssh into one of the computers directly, you first have to ssh to one
of the gates and then from there ssh into your desired computer. Pretty much
the only thing in the gates is ssh(1)
and enable
, the second of which does
a port forward to the computer you need.
That means that every time you work on something locally and you need to put it up to one of the computers for submission, you have to first ssh into the gate, then enable the computer, then sftp into the computer, then ssh from the gate to the computer and submit your files. Since it’s so complicated you didn’t understand it the first time you read it, neither do freshmen on their first semester.
I thought I could help them out in this situation and use the feature of the
public_html
folder that is supplied. This folder gives you your personal
website, under ~username
. It supports a stripped-down, basic version of PHP.
For this reason, I created CSD File
Upload, a simple PHP script that
accepts a file and a password and uploads the file in the user directory
without any more hassle.
The way I was handling the user password was to include its SHA-256 hash in a PHP Variable in the beginning of the file. When the user installed the software, she was executing:
printf "My Desired Password" | sha256sum | cut -d" " -f1
and changing the variable content with the output. As simple as it gets. The script, of course, was only accessible through https.
A day after it is announced in the public mailing lists by the System Administrators, I start getting Pull Requests by students. That’s good news because they seem to care and/or know PHP/git, both of which are not in the official curriculum.
I get a Pull Request from a
user making an “improvement”, and that is putting the password in a separate
file, password.txt
and then reading it from there. Now we all know this is
insecure because that way anyone can visit /password.txt
and get the password
hash.
The proper way to do this would be to create a file password.php
and put in
it:
$PASSWORD = "sha256-goes-here";
and then simply require("password.php");
from the index.php
file. That’s
how the configuration is done in popular PHP software.
A day later, I get another Pull Request, this time including in the message the addressing of security issues. What it did essentially was to employ JavaScript Cryptography to perform the SHA-256 hash before sending it to the server, and also include in the hash the PHP Session ID. With a first look it seems right and that the original implementation was a mistake.
Apart from using JavaScript Crypto, this method is vulnerable to attacks where the attacker knows the password hash. Imagine what would happen if both this and the previous pull request were merged. Now this is possible because doing a client-side hashing of the password is the same as sending the password in plain-text.
When you send the password in plain-text, the system performs an equals
operator or a strcmp()
and allows you to get in. If you hash the password in
the client, and then send it to the server for comparison, then it performs the
same operations just as if it was a plain-text input. That means if I send the
password hash, the system will treat it like it was a plain-text password,
which means that the only thing that happens is that your password from
password123
is now
ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f
, but if you
send the latter, it will let you in.
This does not mean your password is sent over the internet unencrypted. This page works only with HTTPS, which means we let that layer take care of data encryption.
But truth be told, the code wasn’t just sending the password hash. It also employed the PHP Session ID and did something like:
var phpid = "<?php echo session_id(); ?>";
var stringToSubmit = sha256(password + phpid);
Does that offer an additional layer of security? Well, the short answer is no. This nonce was added to ensure that the request could not be replayed, but since the site was working only over https, then replaying wasn’t possible anyways. Does that add an additional layer of security if the site works only over HTTP / compromised HTTPS? The answer is still no. If the user visits the site over HTTP, then the way the attacker was going to get the traffic is Man-In-The-Middle attacks. If you have this kind of access between the user and the HTTP connection, you can alter all the content, modify the JavaScript sent to submit just the password, and in general do whatever you want. No JavaScript can save you from that.
While it seems easy to get software security right, you can never be sure. You have to keep in your head all the possible attack scenarios, even the ones you’re sure won’t happen, and see if your implementation can defend against them (in a reasonable manner). My implementation isn’t perfect, and can be cracked (for example if HTTPS breaks), but in the one hour or two I spent writing the whole I consider this an acceptable risk.
Another conclusion is that by thinking of a better way to do something, making it and submitting it for review, even if it awfully bad or insecure or anything, people are going to see that and they will let you know why so you can learn a lot and benefit from this process. No matter if all your Pull Requests are rejected for various reasons, you should never stop sending them because at least you learn something (new).
At this point I must thank all the people for their contributions to the repository and the time they invested into making the software better.