How to Accept PHPBB Login Information

In Other Words, How PHPBB Passwords Work

Author: Kevin Kurtz    Date:1/24/08    Time: 1:14PM

To accept PHPBB Login Information on your own site, like mine does, seems like a handy thing to do. Your user then need only one set of login information to be able to browse the multiple parts of your site. I use the same login information in (atleast currently) three different places. You sign up in one place and then have the ability to login anywhere with one username and password. It takes the confusion from the user, they only have to remember one thing, and it actually makes your job (as a web admin or web designer) a lot easier. You can just hijack the signup page that PHPBB already uses that has that nice botproof picture and all those other features. Just link your register button to their signup page (usually ucp.php?mode=register in the directory where your PHPBB is stored) and they will collect all the data and store it for you (since they already have to store it for the user to use the forums). All you have to do to allow the users to log in is validate that their username and password match an entry in the phpbb_users table of whatever database you have PHPBB using. This might seem scary, but I'll walk you through it.


The reason it could be a scary process is if you have looked at the phpbb_users table you will notice something specific about the passwords... they are not the passwords you entered for the users (or the users set) they look like a bunch of gibberish. Well, that is basically what this tutorial is going to be about (although I will also show you how to set everything up). The passwords are actually hashed. That means that they are put through a sort of conversion and stored that way. The catch here is there is NO WAY! to convert them back. This is for the safety of the user, someone could get into that table and see the information and still not know their password (if they could change the password they could make it a known value, and it would be much easier to crack that way, so it is not fool proof, but is better than storing the password as the user enters it). How you check to see if their login information is correct is pretty simple, you hash the password they entered and see if it matches the one that PHPBB hashed when they first setup their accounts. Don't Panic! I'm going to walk you through hashing it and give you a really easy function you can just copy and paste into your code to do this.


Let's quickly go over grabbing the information and checking the username... the easy stuff.


<FORM method="post" action="login.php">
Username:<INPUT name="username" /><br />
Password:<INPUT type=password name="password" /><br />
<INPUT TYPE="submit" name="submit" VALUE="Log In!" />
<a href="DirectoryOfForums/ucp.php?mode=register" > &nbsp;&nbsp;Register</a>
</FORM>


LOOKS LIKE:
Username:
Password:
  Register



This was a simple form that grabs a username and password from a user. They hit Log In! and it will send the information in POST form to login.php. I prefer to send login information back to the same page as the login box, and just have an if statement that basically says if they are logged in don't show the box, but that is beyond the scope of this tutorial, for now we will use login.php. The Register button will send them to the register page in your forums. So you don't have to worry about building a register page and storing data, we actually will not have to INSERT any data into the tables, we are just reading the username and password to see if they match. I will also not cover protecting yourself against hacks here. Google SQL Injection to see how to protect your forms from SQL data theft. (or just read about it to learn some hacking skills... whatever)


If you are confused by the <br /> and the / at the end of input tags that is for a good reason. XML requires that every tag have a closing tag. <br> does not (it's deprecated, use my new one for future compliance), there is no such thing as </br>. So you need to add a space and that front slash to tell the tag to be closed by default. This needs to go on every tag that doesnt have a close, this includes inputs and images. The reason for this is that the newer standards require it and it is considered wrong to do it the other way now. Deprecated means that the feature is outdated and wrong (but there is still support for it so that older pages are still readable). Just try to get into the habit of doing it the new way.


Now we will quickly go through the actual checking and then I promise we will get to the passwords part.


<?PHP
//set username and password from posted variable
$username = $_POST['username'];
$password = $_POST['password'];

//this is the database login script. Keep it safe!
$link = mysql_connect('localhost', 'mysqlUsername', 'mysqlPassword');
if (!$link){die('Could not connect: ' . mysql_error());}

//this is the database to access
mysql_select_db('databaseName');

//Now we will query the database for the information we want
//(username_clean is without caps, I check for that too incase they enter it either way)

$query = "SELECT username,user_password FROM phpbb_users WHERE username = '$username' OR username_clean = '$username'";
$result = mysql_query($query);

//If the result is not blank (meaning atleast one user matches that username) continue
if($result != "")
{

//Get a row from the results (should only be one)
$row = mysql_fetch_assoc($result);
//Save username and hashed password from the row
$phpusername = $row['username'];
$phppassword = $row['user_password'];
//Free result. Unnecessary, but frees up memory if you are worried about that
mysql_free_result($result);

//If our password gets hashed and matches phppassword then continue, else Login Failed
if(phpbb_check_hash($password,$phppassword))
{
//Since passwords match we will set SESSION['username']
//You have to have session_start(); at the top (before any html is sent)
//of every page to transfer data in this way.

$_SESSION['username'] = $phpusername;
}
else
{
echo("Login Failed! Sorry.");
}
}
//If the result was empty then there is no such user in the database
else
{
echo("No Such User! Sorry.");
}

//Close the database connection
mysql_close($link); ?>



That does it for login.php, now all you have to do is on all the other pages have session_start(); at the top and if they need to be logged in to view something check if the username is set using if(isset($_SESSION['username'])). That takes care of a down and dirty view of sessions and php. Hope you knew all that stuff to begin with or I just confused the hell out of you. But now we will get onto what the focus of this tutorial really was... What's in that phpbb_check_hash function! That is where the gold of this lies. You can put this function anywhere, but I would put it in a seperate file and just call include('functions.php') somewhere before trying to use the function. That will keep your code cleaner. I would also put the database login information in a seperate file, because I use databases a lot and its easy to just say include('databaselogin.php') then write all that login info out every time, then I can change the password in one place.



IN THE EVENT YOU DON'T CARE TO KNOW WHAT IS GOING ON BEHIND THE SCENES, JUST COPY AND PASTE THESE THREE FUNCTIONS INTO A SEPERATE FILE AS EXPLAINED ABOVE AND CALL THAT FILE BEFORE CHECKING TO SEE IF THE PASSWORDS MATCH. READ ON IF YOU LIKE, BUT IN NO WAY ARE YOU OBLIGATED TO KNOW WHAT IS GOING ON BELOW TO MAKE THE PASSWORDS WORK!

/* I added a BOATLOAD of comments to these functions and removed everything you don't need. The phpbb team doesn't comment well. If the comment isn't mine it is marked as such (only one spot)

This fuction will take in the user supplied password and the hashed password from the database and see if they match. It will return either true or false*/

function phpbb_check_hash($password, $hash)
{
//just some hashing string the phpbb crew came up with (original huh?)
$itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

//If the hashed password is not 34 characters then it is just an md5 hash
if (strlen($hash) == 34)
{
return (_hash_crypt_private($password, $hash, $itoa64) === $hash) ? true : false;
}

//In the Event of it not being 34 characters it is an md5 hash, so we will check to see if it matches that and then return true or false. === means Identical, not just equal but of the same type.
//Only gets here if the hash IS NOT 34 characters in length

return (md5($password) === $hash) ? true : false;
}


//Now you should be curious about that _hash_crypt_private function. Well we have that too.
//You don't need to be worried about what is in this function, just call the phpbb_check_hash and it takes care of this, but you do need to put it in that functions.php file along with it.

/**
* The crypt function/replacement
*/

function _hash_crypt_private($password, $setting, &$itoa64)
{
$output = '*';

// Check for correct hash (password hashes always start with $H$
if (substr($setting, 0, 3) != '$H$')
{
return $output;
}

//This just says that the 4th character in the hashed password must be between 4 and Q in itoa64
//This is just another way of making sure the hash was made by phpbb

$count_log2 = strpos($itoa64, $setting[3]);

if ($count_log2 < 7 || $count_log2 > 30)
{
return $output;
}

/*Shift the bits of 00000001 $count_log2 bits to the left. Meaning if above we made count_log2 4 (it can't be 4 or it would have gotten thrown out above, but this is just an example, count would become 00010000 which equals 16... don't ask questions, I was just telling you what it was doing.*/
$count = 1 << $count_log2;

//Salt will now equal the 5-12 characters of out hashed password.
$salt = substr($setting, 4, 8);


//If by some magical miracle setting was not 12 characters long, then salt would be less than 8, so we check
if (strlen($salt) != 8)
{
return $output;
}

//Next part is a note from the phpbb team, not me! /**
* We're kind of forced to use MD5 here since it's the only
* cryptographic primitive available in all versions of PHP
* currently in use. To implement our own low-level crypto
* in PHP would result in much worse performance and
* consequently in lower iteration counts and hashes that are
* quicker to crack (by non-PHP code).
*/

//if phpversion is 5 or above use md5 to create the hash. The string to hash is salt + original password... the period means cat or put strings together.

if (PHP_VERSION >= 5)
{
$hash = md5($salt . $password, true);
do
{
$hash = md5($hash . $password, true);
}
while (--$count);
}
else
//in the event you have php4 or below, have to use a seperate method. All these functions are built into php so look in the source of that for documentation please. {
$hash = pack('H*', md5($salt . $password));
do
{
$hash = pack('H*', md5($hash . $password));
}
while (--$count);
}

/*Output (what we are returning equals the first 12 characters of our hashed password (that we passed this function) catted (string add remember) with the results of _hash_encode64 (YAY, we are introducting another function... this is the last one, I promise)*/
$output = substr($setting, 0, 12);
$output .= _hash_encode64($hash, 16, $itoa64);

//Finally return the output back to phpbb_check_hash
return $output;
}

/*
* Encode hash (I AM NOT EXPLAINING THIS ONE IN DETAIL... just let it do its job. Copy and paste if you don't already understand it.
*/

function _hash_encode64($input, $count, &$itoa64)
{
$output = '';
$i = 0;

do
{
$value = ord($input[$i++]);
$output .= $itoa64[$value & 0x3f];

if ($i < $count)
{
$value |= ord($input[$i]) << 8;
}

$output .= $itoa64[($value >> 6) & 0x3f];

if ($i++ >= $count)
{
break;
}

if ($i < $count)
{
$value |= ord($input[$i]) << 16;
}

$output .= $itoa64[($value >> 12) & 0x3f];

if ($i++ >= $count)
{
break;
}

$output .= $itoa64[($value >> 18) & 0x3f];
}
while ($i < $count);

//return results back to _hash_crypt_private
return $output;
}


Hopefully, if you read my comments, you have a decent grip on what is going on in there, but honestly you dont need to know any of it. If you just grab those three functions and put them in some seperate file, then just call that file when you go to log in the user you will be fine. The only function you even need to call is php_check_hash(password, hashed_password_from_table) as explained above in the checking code. I wrote this too help someone do this more easily than I did it. I had to dig through the source of PHPBB3 to find what I needed. I just cut it all out, added a jesus-load of comments to explain what is happening, and got rid of the unnecessary stuff so that you guys could just copy and paste if needed. Now in the event you actually want to read through that functions file and understand what is going on, I hope my comments helped clarify some things, the phpbb team is not much on comment their code so I tried to explain it as best as I could to a beginner, but I could have missed some stuff. I have been using PHP for some time so I might have assumed you knew certain things about it, I tried to explain things thoroughly when I saw that they might be confusing to a newbie.


In the event that I did manage to confuse you in any way, just leave a comment or shoot me an e-mail and I will be glad to clarify or help you out if something is not working. I got it working just fine using only the stuff above (the login on this site uses my forums login information). If you need help figuring something else out I would be glad to help as well. You can post any questions in the forum (so others can be helped by them later) or you can e-mail me and I will do my best. Hope I helped someone, enjoy having a single login for all of your sites... and if you have questions on SESSION or POST data I would be happy to explain that as well, but odds are if you don't know the basics of PHP you didn't get much of anywhere in this tutorial. E-mail is in the footer of every page on this site. Kevin [at] KreepyIndividual [dot] com.



Comments:



Please Login if you would like to post a comment, or signup here