Building Browsergames: The Registration Page (Perl)

Yesterday, I introduced you to building your registration page using the popular language PHP. Today, I’ll be doing the same thing – except in Perl.

I’m assuming that you’ve already set up your database according to the instructions from yesterday. If you haven’t, here’s the SQL you need to run on a new database:

CREATE TABLE users (
	id int NOT NULL AUTO_INCREMENT,
	username varchar(250),
	password varchar(50),
	PRIMARY KEY(id)
);

Once that’s done, we can get to writing our Perl. First off, you’ll want to create a new .cgi file, and put this code into it to create our starter form:

 

#!/usr/bin/perl
 
use strict;		# these will help you catch errors easier
use warnings;		# see above
use CGI qw(:cgi);
use CGI::Carp qw(fatalsToBrowser warningsToBrowser);
use DBI;
 
my $query = new CGI;
my $html;
$html .= qq ~
<form method='post' action='register.cgi'>
Username: <input type='text' name='username' /><br />
Password: <input type='password' name='password' /><br />
Confirm Password: <input type='password' name='confirm' /><br />
<input type='submit' value='Register!' />
</form>
~;
 
print $query->header();
print $html;

This sets up our basic script. We use the strict pragma, and the CGI and DBI modules. strict will help us in debugging and catch minor mess-ups for us. CGI will help us do all of our web stuff like form handling and header printing, and DBI is how we will be connecting to our database. The CGI::Carp module will make it so that any error messages that appear are printed out to our browser, instead of just causing us headaches.

Next, we create a new CGI object and store it into $query. We will be using this CGI object to print our header later(and once we get to doing a login page, we’ll be using it to print cookies).

Once we have our CGI object set up, we create the variable $html, and begin storing the HTML we will be outputting into it. We use the qq operator so that we can store multiple lines of text easily, along with interpolating variables into it. We also use the .= operator to concatenate text onto our $html variable, so that when we’re adding error messages later we can add it all to one variable without any of it over-writing the other – which means that at the end of our code we only need to spit out the contents of $html. After that’s finished, we print a header using our CGI object(by default, it prints a text/html header), and then print our HTML output.

Once that’s finished, we’ll need to add some code to handle when our form is submitted. Following the same order as yesterday’s post, we’ll check if the password and the confirmation password match first.

my %arguments = $query->Vars;
my $html;
 
if(%arguments) {
	if($arguments{password} ne $arguments{confirm}) {
		$html .= qq ~
<span style='color:red'>Error! Those passwords do not match!</span>	
		~;	
	}
}
 
$html .= qq ~

In this piece of code, we start by storing our arguments into the hash %arguments, by calling the method Vars on $query. This stores all posted arguments into our hash, with their names as keys in the hash. Once that’s finished, we do a basic check to make sure that %arguments is not empty before we start performing operations with it. Inside our case when %arguments does have things inside of it, we check to see if the passwords are equal to each other. If they aren’t, we concatenate a string onto the beginning of $html, with our helpful error message inside.

Once the password testing is done, we can move on to checking if the requested username is available. In order to do that, we’ll need to connect to our database using the DBI module:

} else {
		my $dbhost = 'localhost';
		my $dbname = 'name';
		my $dbuser = 'user';
		my $dbpass = 'pass';
		my $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost",$dbuser,$dbpass,{RaiseError => 1});
	}

This code is virtually a direct copy of the PHP from yesterday, minus a few language-specific changes. After storing our connection values(make sure to change these to the ones for your server!), we call DBI->connect() to connect to our database, using the ‘mysql’ set of drivers. The database connection handle that is returned is then stored into $dbh. We pass in the argument {RaiseError => 1} so that we won’t have to add or die() after each database operation we want to perform.

Once we’ve connected, we can begin running queries on our database. This is how we would check to see if the username is already taken:

my $sth = $dbh->prepare("SELECT count(id) FROM users WHERE UPPER(username) = UPPER(?)");
		$sth->execute($arguments{username});
		my $count;
		$sth->bind_columns(\$count);
		$sth->fetch;
		if($count >= 1) {
			$html .= qq ~
<span style='color:red'>Error: that username is taken.</span>			
			~;	
		}
	}

In this code, we prepare our query, execute it with the username parameter sent to us by the user, and then retrieve the count into $count. We don’t need to worry about SQL Injection because, by using the prepare()->execute()->fetch method, the DBI module will quote all of our arguments for us automatically. It’s pretty handy that way.

After we fetch the count, we check to see if there is 1 or more users who are using the same username – and if there are, we add a helpful error message to $html.

At this point, our registration form is just about finished – all we’re missing is actually adding the user to the database. Let’s add that code:

 

} else {
			$sth = $dbh->prepare("INSERT INTO users(username,password) VALUES (?,?)");
			$sth->execute($arguments{username},crypt($arguments{password},$arguments{username}));
			$html .= qq ~
<span style='color:green'>Congratulations! You registered successfully!</span>			
			~;	
		}

And once that last piece of code has been added, our registration form is ready to go! As you can see, there isn’t that much that’s fundamentally different from the PHP registration page. One notable exception, however, is the use of Perl’s crypt() function, to hash the user’s password. We passed in the user’s username and the user’s password to crypt(), so that it would hash the user’s password by using their username as the salt to hash it with.

Why are we hashing the user’s passwords? Because you should absolutely never store a password in plaintext – it’s just asking for trouble. If a malicious user somehow gets access to your database and you’ve stored user’s passwords in plaintext, they’ll have access to everyone’s passwords – which is a massive security flaw. By hashing the passwords, you make sure that that can’t happen – so even if they did get access to your database, they wouldn’t be able to break into anyone’s accounts without actually managing to guess or brute force the user’s password.

Once the user has been inserted, we display a nice green message telling them they registered successfully.

And that’s all there is to it – your own registration page, written in Perl with CGI. Here’s the full code:

#!/usr/bin/perl
 
use strict;		# these will help you catch errors easier
use warnings;		# see above
use CGI qw(:cgi);
use CGI::Carp qw(fatalsToBrowser warningsToBrowser);
use DBI;
 
my $query = new CGI;
my %arguments = $query->Vars;
my $html;
 
if(%arguments) {
	if($arguments{password} ne $arguments{confirm}) {
		$html .= qq ~
<span style='color:red'>Error! Those passwords do not match!</span>	
		~;	
	} else {
		my $dbhost = 'localhost';
		my $dbname = 'name';
		my $dbuser = 'user';
		my $dbpass = 'pass';
		my $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost",$dbuser,$dbpass,{RaiseError => 1});
		my $sth = $dbh->prepare("SELECT count(id) FROM users WHERE UPPER(username) = UPPER(?)");
		$sth->execute($arguments{username});
		my $count;
		$sth->bind_columns(\$count);
		$sth->fetch;
		if($count >= 1) {
			$html .= qq ~
<span style='color:red'>Error: that username is taken.</span>			
			~;	
		} else {
			$sth = $dbh->prepare("INSERT INTO users(username,password) VALUES (?,?)");
			$sth->execute($arguments{username},crypt($arguments{password},$arguments{username}));
			$html .= qq ~
<span style='color:green'>Congratulations! You registered successfully!</span>			
			~;	
		}
	}
}
 
$html .= qq ~
<form method='post' action='register.cgi'>
Username: <input type='text' name='username' /><br />
Password: <input type='password' name='password' /><br />
Confirm Password: <input type='password' name='confirm' /><br />
<input type='submit' value='Register!' />
</form>
~;
 
print $query->header();
print $html;

If you want to see it in action, you can play with it here.