PHP, Mysql and Authentication 101
PHP, Mysql and Authentication 101
(Page 1 of 5 )
Authentication is required by any online admin system. In this article, Havard introduces us to two types
of PHP/MySQL authentication: dialogs and forms.When you're making a dynamic site in PHP, you might
want to restrict an area from normal users and grant access only to a set of trusted users. You wouldn't
want to make the admin area open for everybody now, would you?
To restrict an area you need to create some sort of authentication method, and how you do this is one of
the questions I've seen get asked a lot. Throughout this article I intend to show you a few different
approaches to authentication with PHP and MySQL.
(Page 2 of 5 )
We will be making use of a MySQL database to store the usernames and passwords of our authenticated
users. Firstly we will have to set up the database and it's respective tables. Run this set of commands
through the MySQL console application:
The code above creates a database containing a table named users. We made the userName column a
unique key to prevent having two users with the same username. Let's insert a user into the database, so
we have something to authenticate against:
You may want to change the values for the username and password. The MD5() function is a built-in
MySQL function, which calculates a 128 bit checksum for the provided string. The returned string is 32
characters long, hence we used VARCHAR(32) for the userPass column. We will be using this table
through the whole article.
Now that we've created the database, table and a user, we can continue.
You should have PHP version 4.1.0 or above. If you have an earlier version you'll have to rewrite some of
the code. This is because I'm using super global arrays such as $_SESSION and $_SERVER, which were
introduced in PHP version 4.1.0.
HTTP Authentication
If PHP is installed as an Apache module, thene you can use PHP's HTTP Authentication hook to pop up a
username/password authentication window in the browser. This is done by sending some special
parameters in the header() function. When the user has filled in both the username and password fields,
the values can be accessed within a PHP script using the variables $PHP_AUTH_USER and
1
$PHP_AUTH_PW.
Remember that this type of authentication only works when PHP is installed as an apache-module, which
means that if you are using the CGI version, you can skim through this part of the article as we'll be
discussing authentication through forms on the next page.
<?PHP
function displayLogin() {
header("WWW-Authenticate: Basic realm=\"My Website\"");
header("HTTP/1.0 401 Unauthorized");
echo "<h2>Authentication Failure</h2>";
echo "The username and password provided did not work. Please reload this page and try again.";
exit;
}
if (!isset($PHP_AUTH_USER) || !isset($PHP_AUTH_PW)) {
// If username or password hasn't been set, display the login request.
displayLogin();
} else {
// Escape both the password and username string to prevent users from inserting bogus data.
$PHP_AUTH_USER = addslashes($PHP_AUTH_USER);
$PHP_AUTH_PW = md5($PHP_AUTH_PW);
if (!$num) {
// If there were no matching users, show the login
displayLogin();
}
}
?>
The code above produces a dialog authentication window, which looks like this:
2
(Page 3 of 5 )
function displayLogin() {
header("WWW-Authenticate: Basic realm=\"My Website\"");
header("HTTP/1.0 401 Unauthorized");
echo "<h2>Authentication Failure</h2>";
echo "The username and password provided did not work. Please reload this page and try again.";
exit;
}
This function is called when either $PHP_AUTH_USER or $PHP_AUTH_PW isn't set, and when the
MySQL query didn't return anything. The first header calls the browser's authentication window, while the
second header tells the browser what type of error has occurred. Everything between the last header and
"exit;" will be displayed to the user in case the authentication failed, or cancel was pressed in the
authentication window.
The realm name must remain the same on all of your pages. If it doesn't, the browser will require
authentication for all unvisited realms.
if (!isset($PHP_AUTH_USER) || !isset($PHP_AUTH_PW)) {
// If username or password hasn't been set, display the login request.
displayLogin();
} else {
// Escape both the password and username string to prevent users from inserting bogus data.
$PHP_AUTH_USER = addslashes($PHP_AUTH_USER);
$PHP_AUTH_PW = md5($PHP_AUTH_PW);
3
if (!$num) {
// If there were no matching users, show the login
displayLogin();
}
}
In this code we check if $PHP_AUTH_USER or $PHP_AUTH_PW hasn't been set. If they haven't been
set, then we call the displayLogin() function. If both the username and password have been set, we
authenticate them against our database. By the way, we're now using the bult-in md5 function in PHP to
create a md5 checksum, instead of using the MySQL function.
If the user wasn't found in the database, we call the displayLogin() function.
We use the addslashes() function to escape the variables that are used in the MySQL query. By doing this,
we prevent the user from entering bogus data, which in the worst case could cause havoc on your
database.
All code below the if construct will only be displayed to authenticated users.
Place the code above in a .php file, and include it in every page you want authentication on. This way you
only have to edit one file in case you need to make some changes to the authentication code.
if ($_REQUEST['logout'] == true) {
// To logout a user, you can just use the displayLogin() function and resend the authentication headers.
displayLogin();
}
By calling the displayLogin() function when the user is already logged in, we cause the browser to display
the authentication window, and clear any previous successful authentication. This works on most
browsers. To log out with the code above you can add ?logout=true to the URL.
The only problem I can see with this type of authentication is that it's not available in the CGI version of
PHP. Although most servers run PHP as a module, some don't, and that would mean trouble for your
authentication script. Continue reading to learn another approach.
(Page 4 of 5 )
If you would like a more aesthetic approach to authentication, you may want to allow the user to log in
using a HTML form. This is probably the most popular approach. We will be using sessions, so the user
doesn't have to re-authenticate on every page that requires authentication.
<?PHP
4
// Add slashes to the username, and make a md5 checksum of the password.
$_POST['user'] = addslashes($_POST['user']);
$_POST['pass'] = md5($_POST['pass']);
if (!$num) {
} else {
echo "<h1>Congratulations</h1>";
echo "You're now logged in. Try visiting <a href='page2.php'>Page 2</a>.";
?>
// Add slashes to the username, and make a md5 checksum of the password.
$_POST['user'] = addslashes($_POST['user']);
$_POST['pass'] = md5($_POST['pass']);
5
$result = mysql_query("SELECT count(id) FROM users WHERE password='$_POST[pass]' AND
username='$_POST[user]'") or die("Couldn't query the user-database.");
$num = mysql_result($result, 0);
if (!$num) {
This code connects to the database, and prepares the variables for the SQL query. After the data is
prepared, we're querying the database for the information entered in the form. If the query doesn't return
anything, we display the login form. Instead of hard coding the form, you could also make the form a
.html file, and just include() it.
} else {
echo "<h1>Congratulations</h1>";
echo "You're now logged in. Try visiting <a href='page2.php'>Page 2</a>.";
This part gets executed when the information entered matched a user. We're starting a session through
using the session_start() function, and then we're adding the session variables $_SESSION['user'] and
$_SESSION['pass']. Since we've already added the slashes, and made the password an MD5 checksum,
we'll just add them as they are. By the way, since we're using sessions, the login information will be
deleted when you exit your browser. You may implement a normal cookie here too, so that it stays on
your machine until it either expires, or the user deletes it manually.
Since there hasn't been any output anything to the browser just yet, we can redirect the user using header()
redirection instead of displaying text. Just replace the text with this: header('Location: page2.php');
Now it's time to take a look at page2.php, which we linked to from login.php. Insert the following code
into a file called page2.php:
6
<?PHP
if (!$_SESSION['user'] || !$_SESSION['pass']) {
} else {
if (!$num) {
// If the credentials didn't match,
// redirect the user to the login screen.
header('Location: login.php');
die();
}
}
?>
if (!$_SESSION['user'] || !$_SESSION['pass']) {
In this snippet we're checking to see if the session variables have been set. If they haven't, then we redirect
them to the login.php again. In case you're wondering why we're using die after the header(), it's for extra
security. A hacker can for example make his own browser that ignores header redirects. Better safe than
sorry.
} else {
if (!$num) {
// If the credentials didn't match,
// redirect the user to the login screen.
header('Location: login.php');
die();
}
}
This code is almost exactly the same as login.php. We don't have to add slashes here because they were
already added in login.php. Again, you can see we're using die() after the header() redirect.
This is just placeholder text. Feel free to replace it with whatever you want.
Try authenticating yourself, and see how the session transfers the login information between the pages.
Instead of copying the code in page2.php into all pages you want authentication on, you can name it
auth.php, and include() it in all of the pages you want authentication on.
All you have to do to delete the session data, thus logging yourself out, is to make a PHP script with this
code:
<?PHP
session_start();
8
session_destroy();
(Page 5 of 5 )
You should now be able to protect your pages using PHP/MySQL authentication. Once you get into it, you'll see for
yourself how valuable this can be. You may also have learned some precautions to take when querying a database.
Basic rule: Always prepare variables before using them in SQL queries.
I could have included all of this functionality in the tutorial, but if I did that, there wouldn't be any fun left for you. As
always, feel free to ask questions or discuss this article in the forums.