Page 1 of 1

Login script tutorial

Posted: Sun Apr 01, 2012 11:59 am
by califdon
social_experiment wrote:
Celauran wrote: and I'm surprised this hasn't already been mentioned
This type of advice (a basic login how-to) should be a sticky thread somewhere; containing do's and don'ts about login scripts; it would imo be a great help to beginners
So write one for us! :D

Re: Login script tutorial

Posted: Sun Apr 01, 2012 4:06 pm
by social_experiment
Challenge accepted :) I'll look through the forum for the login type scripts and put together something useful

Re: Login script tutorial

Posted: Sun Apr 01, 2012 5:24 pm
by califdon
social_experiment wrote:Challenge accepted :) I'll look through the forum for the login type scripts and put together something useful
Great! I fully agree that it will be a very useful article, both for beginner readers to stumble onto and for us to refer others to instead of trying to explain the whole subject over and over again. Many thanks for the effort!

Re: Login script tutorial

Posted: Mon Apr 02, 2012 1:45 pm
by Celauran
I had actually been considering the same thing since social_experiment suggested it. Here is a list to get the ball rolling:

DO
  • Use bcrypt or PHPass to hash your passwords.
  • Make sure your password field is long enough to contain the hash.
  • Escape anything you plan on passing into a query. Never trust user input.
DON'T
  • Use mysql_ functions. They've been deprecated since 2004. Stop it already.
  • Store passwords in plain text.
  • Store passwords as md5 hashes.
  • Store passwords in cookies or session data
  • Place upper limits on password length. Lower limits are OK.
I've also included (admittedly rudimentary) sample registration and login scripts.

First, the database schema:

Code: Select all

CREATE TABLE `users` (
    `id` INT(11) UNSIGNED ZEROFILL PRIMARY KEY AUTO_INCREMENT,
    `username` VARCHAR(50) NOT NULL,
    `password` VARCHAR(60) NOT NULL,
    `email` VARCHAR(80) NOT NULL,
    `created` DATETIME NOT NULL
)
CHARACTER SET = UTF8,
COLLATE utf8_general_ci,
ENGINE = InnoDB;
Registration page:

Code: Select all

<?php

/**
 * Don't use mysql_ functions. These are for MySQL 4.x and have been deprecated
 * since 2004. MySQLi is fine if you know you'll only be using MySQL databases.
 * PDO doesn't tie you to a specific RDBMS.
 */
$sql = new PDO('mysql:host=localhost;dbname=whatever;', 'username', 'password');

// Create an array to catch any errors in the registration form.
$errors = array();

/**
 * Make sure the form has been submitted before trying to process it. This is
 * the single most common cause of 'undefined index' notices.
 */
if (!empty($_POST))
{
    // First check that required fields have been filled in.
    if (empty($_POST['username']))
    {
        $errors['username'] = "Username cannot be empty.";
    }

    // OPTIONAL
    // Restrict usernames to alphanumeric plus space, dot, dash, and underscore.
    /*
    if (preg_match('/[^a-zA-Z0-9 .-_]/', $_POST['username']))
    {
        $errors['username'] = "Username contains illegal characters.";
    }
    */

    if (empty($_POST['password']))
    {
        $errors['password'] = "Password cannot be empty.";
    }

    /**
     * Note there's no upper limit to password length.
     * This was meant to be 8 characters, but phpBB insists on turning that
     * into a smiley.
     */
    if (strlen($_POST['password']) < 7)
    {
        $errors['password'] = "Password must be at least 8 charcaters.";
    }

    if (empty($_POST['password_confirm']))
    {
        $errors['password_confirm'] = "Please confirm password.";
    }

    if ($_POST['password'] != $_POST['password_confirm'])
    {
        $errors['password'] = "Passwords do not match.";
    }

    $email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
    if (!$email)
    {
        $errors['email'] = "Not a valid email address.";
    }

    /**
     * Check that the username and email aren't already in our database.
     * Note the use of prepared statements. If you aren't using prepared
     * statements, be sure to escape your data before passing it to the query.
     * 
     * Note also the absence of SELECT *
     * Grab the columns you need, nothing more.
     */
    $query = "SELECT username, email
              FROM users
              WHERE username = :username OR email = :email";
    $stmt = $sql->prepare($query);
    $stmt->execute(array(
        ':username' => $_POST['username'],
        ':email' => $email
    ));

    /**
     * There may well be more than one point of failure, but all we really need
     * is the first one.
     */
    $existing = $stmt->fetchObject();

    if ($existing)
    {
        if ($existing->username == $_POST['username'])
        {
            $errors['username'] = "That username is already in use.";
        }
        if ($existing->email == $email)
        {
            $errors['email'] = "That email address is already in use.";
        }
    }
}

/**
 * If the form has been submitted and no errors were detected, we can proceed
 * to account creation.
 */
if (!empty($_POST) && empty($errors))
{
    /**
     * I'm using this rather than using NOW() in the SQL query because it
     * doubles as a handy salt.
     */
    $date = new DateTime();

    /**
     * I'm going to mention it again because it's important; if you aren't using
     * prepared statements, be sure to escape your data before passing it to
     * your query. Relevant: http://xkcd.com/327/
     */
    $query = "INSERT INTO users (username, password, email, created)
              VALUES (:username, :password, :email, :created)";
    $stmt = $sql->prepare($query);
    $success = $stmt->execute(array(
        ':username' => $_POST['username'],
        ':password' => hashPassword($_POST['password'], $date->format('U')),
        ':email'    => $_POST['email'],
        ':created'  => $date->format('Y-m-d H:i:s'),
    ));

    if ($success)
    {
        $message = "Account created.";
    }
    else
    {
        $errors['registration'] = "Account could not be created. Please try again later.";
    }
}

?>

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>User Registration</title>
    </head>
    <body>
        <?php if (isset($message)): ?>
        <p class="success"><?php echo $message; ?></p>
        <?php endif; ?>

        <!-- Note that we're again checking that each array key exists before
             trying to use it, in order to prevent undefined index notices. -->
        <?php if (isset($errors['registration'])): ?>
        <p class="error"><?php echo $errors['registration']; ?></p>
        <?php endif; ?>

        <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">
            <fieldset id="registration">
                <label for="username">Username</label>
                <input type="text" id="username" name="username" />
                <span class="error">
                    <?php echo isset($errors['username']) ? $errors['username'] : ''; ?>
                </span><br />

                <label for="email">Email Address</label>
                <input type="text" id="email" name="email" />
                <span class="error">
                    <?php echo isset($errors['email']) ? $errors['email'] : ''; ?>
                </span><br />

                <label for="password">Password</label>
                <input type="password" id="password" name="password" />
                <span class="error">
                    <?php echo isset($errors['password']) ? $errors['password'] : ''; ?>
                </span><br />

                <label for="password_confirm">Confirm Password</label>
                <input type="password" id="password_confirm" name="password_confirm" />
                <span class="error">
                    <?php echo isset($errors['password_confirm']) ? $errors['password_confirm'] : ''; ?>
                </span><br />

                <input type="submit" value="Submit" />
            </fieldset>
        </form>
    </body>
</html>

<?php

/**
 * We're using bcrypt to hash our passwords. Read why:
 * http://codahale.com/how-to-safely-store-a-password/
 *
 * If CRYPT_BLOWFISH isn't available to you, I strongly recommend using PHPass:
 * http://www.openwall.com/phpass/
 *
 * @param string $password The user-provided password.
 * @param string $salt     Blowfish salt; account creation timestamp in this example.
 * @return string          Hashed password
 */
function hashPassword($password, $salt)
{
    return crypt($password, '$2a$12$' . substr(md5($salt), 0, 22));
}

?>
Login page:

Code: Select all

<?php

session_start();

// User is already logged in. Redirect them somewhere useful.
if (isset($_SESSION['id']))
{
    header('Location: foo');
    exit();
}

$sql = new PDO('mysql:host=localhost;dbname=whatever;', 'username', 'password');

if (!empty($_POST))
{
    $query = "SELECT id, password, UNIX_TIMESTAMP(created) AS salt
              FROM users
              WHERE username = :username";
    $stmt = $sql->prepare($query);
    $stmt->execute(array(':username' => $_POST['username']));
    $user = $stmt->fetchObject();

    /**
     * Check that the query returned a result (otherwise user doesn't exist)
     * and that provided password is correct.
     */
    if ($user && $user->password == hashPassword($_POST['password'], $user->salt))
    {
        /**
         * Set cookies here if/as needed.
         * Set session data as needed. DO NOT store user's password in
         * cookies or sessions!
         * Redirect the user if/as required.
         */
        $_SESSION['id'] = $user->id;
    }
    /**
     * Don't provide specific details as to whether username or password was
     * incorrect. If an attacker knows they've found a valid username, you've
     * just made their life easier.
     */
    else
    {
        $error = "Login failed.";
    }
}

?>

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>User Login</title>
    </head>
    <body>
        <?php if (isset($error)): ?>
        <p class="error"><?php echo $error; ?></p>
        <?php endif; ?>

        <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">
            <fieldset id="login">
                <label for="username">Username</label>
                <input type="text" id="username" name="username" /><br />

                <label for="password">Password</label>
                <input type="password" id="password" name="password" /><br />

                <input type="submit" value="Login" />
            </fieldset>
        </form>
    </body>
</html>

<?php

function hashPassword($password, $salt)
{
    return crypt($password, '$2a$12$' . substr(md5($salt), 0, 22));
}

?>
These are pretty basic and could certainly have more functionality added to them, but they serve well enough as a jumping off point. Yes, there's code duplication; I wanted to keep the number of files I created here to a minimum while still having something that would work when copy/pasted.

Re: Login script tutorial

Posted: Mon Apr 02, 2012 2:14 pm
by califdon
Nothing wrong with collaboration. If you 2 can agree on a tutorial, great! If you can't, I don't think having 2 would be all that bad. I do think we need to move this discussion to the General Discussion forum, we've drifted off the topic of the original poster's question, so I've left the original topic and answers related to it in PHP - Code and moved the last 5 posts, including this one, to General Discussion.

My own comments, for whatever they may be worth, on Celauran's proposed draft:
  • I would suggest listing the Do's before the Don't's. My eye skipped over the word "Don't" and I first thought he was suggesting that we should use mysql_ functions, etc.
  • I agree with the recommendations regarding the use of PDO, but I think a lot of beginners may not choose to do so, and that makes the script more confusing, with prepared statements, etc. My suggestion would be to explain that there are several approaches to RDMS choice and then have separate scripts for each (yes, more scripts, longer tutorial, but much clearer to a beginner).
Now those are only one person's opinion, so if you 2 agree on something different, it's fine with me.

Re: Login script tutorial

Posted: Mon Apr 02, 2012 2:39 pm
by Celauran
califdon wrote:I would suggest listing the Do's before the Don't's. My eye skipped over the word "Don't" and I first thought he was suggesting that we should use mysql_ functions, etc.
That's a good point. I've gone and changed that.
califdon wrote:I agree with the recommendations regarding the use of PDO, but I think a lot of beginners may not choose to do so, and that makes the script more confusing, with prepared statements, etc. My suggestion would be to explain that there are several approaches to RDMS choice and then have separate scripts for each (yes, more scripts, longer tutorial, but much clearer to a beginner).
I've modified the scripts above slightly to use MySQLi and PHPass. Obviously, you'll need to download PHPass for this to work. I hope it's clear enough that they can be 'mixed and matched', if you will; PDO + PHPass will work fine, MySQLi + bcrypt as well.

Register page:

Code: Select all

<?php

include 'PasswordHash.php';

/**
 * Don't use mysql_ functions. These are for MySQL 4.x and have been deprecated
 * since 2004. MySQLi is fine if you know you'll only be using MySQL databases.
 * PDO doesn't tie you to a specific RDBMS.
 */
$sql = new mysqli('localhost', 'username', 'password', 'database_name');

// Create an array to catch any errors in the registration form.
$errors = array();

/**
 * Make sure the form has been submitted before trying to process it. This is
 * single most common cause of 'undefined index' notices.
 */
if (!empty($_POST))
{
    // First check that required fields have been filled in.
    if (empty($_POST['username']))
    {
        $errors['username'] = "Username cannot be empty.";
    }

    // OPTIONAL
    // Restrict usernames to alphanumeric plus space, dot, dash, and underscore.
    /*
    if (preg_match('/[^a-zA-Z0-9 .-_]/', $_POST['username']))
    {
        $errors['username'] = "Username contains illegal characters.";
    }
    */

    if (empty($_POST['password']))
    {
        $errors['password'] = "Password cannot be empty.";
    }

    /**
     * Note there's no upper limit to password length.
     * This was meant to be 8 characters, but phpBB insists on turning that
     * into a smiley.
     */
    if (strlen($_POST['password']) < 7)
    {
        $errors['password'] = "Password must be at least 8 charcaters.";
    }

    if (empty($_POST['password_confirm']))
    {
        $errors['password_confirm'] = "Please confirm password.";
    }

    if ($_POST['password'] != $_POST['password_confirm'])
    {
        $errors['password'] = "Passwords do not match.";
    }

    $email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
    if (!$email)
    {
        $errors['email'] = "Not a valid email address.";
    }

    /**
     * Escape the data we're going to use in our query. Never trust user input.
     */
    $username = $sql->real_escape_string($_POST['username']);
    $email    = $sql->real_escape_string($email);

    /**
     * Check that the username and email aren't already in our database.
     *
     * Note also the absence of SELECT *
     * Grab the columns you need, nothing more.
     */
    $query  = "SELECT username, email
               FROM users
               WHERE username = '{$username}' OR email = '{$email}'";
    $result = $sql->query($query);

    /**
     * There may well be more than one point of failure, but all we really need
     * is the first one.
     */
    $existing = $result->fetch_object();

    if ($existing)
    {
        if ($existing->username == $_POST['username'])
        {
            $errors['username'] = "That username is already in use.";
        }
        if ($existing->email == $email)
        {
            $errors['email'] = "That email address is already in use.";
        }
    }
}

/**
 * If the form has been submitted and no errors were detected, we can proceed
 * to account creation.
 */
if (!empty($_POST) && empty($errors))
{
    /**
     * Hash password before storing in database
     */
    $hasher = new PasswordHash(8, FALSE);
    $password = $hasher->HashPassword($_POST['password']);

    $query = "INSERT INTO users (username, password, email, created)
              VALUES ('{$username}', '{$password}', '{$email}', NOW())";
    $success = $sql->query($query);

    if ($success)
    {
        $message = "Account created.";
    }
    else
    {
        $errors['registration'] = "Account could not be created. Please try again later.";
    }
}

?>

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>User Registration</title>
    </head>
    <body>
        <?php if (isset($message)): ?>
        <p class="success"><?php echo $message; ?></p>
        <?php endif; ?>

        <!-- Note that we're again checking that each array key exists before
             trying to use it, in order to prevent undefined index notices. -->
        <?php if (isset($errors['registration'])): ?>
        <p class="error"><?php echo $errors['registration']; ?></p>
        <?php endif; ?>

        <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">
            <fieldset id="registration">
                <label for="username">Username</label>
                <input type="text" id="username" name="username" />
                <span class="error">
                    <?php echo isset($errors['username']) ? $errors['username'] : ''; ?>
                </span><br />

                <label for="email">Email Address</label>
                <input type="text" id="email" name="email" />
                <span class="error">
                    <?php echo isset($errors['email']) ? $errors['email'] : ''; ?>
                </span><br />

                <label for="password">Password</label>
                <input type="password" id="password" name="password" />
                <span class="error">
                    <?php echo isset($errors['password']) ? $errors['password'] : ''; ?>
                </span><br />

                <label for="password_confirm">Confirm Password</label>
                <input type="password" id="password_confirm" name="password_confirm" />
                <span class="error">
                    <?php echo isset($errors['password_confirm']) ? $errors['password_confirm'] : ''; ?>
                </span><br />

                <input type="submit" value="Submit" />
            </fieldset>
        </form>
    </body>
</html>
Login page:

Code: Select all

<?php

session_start();

// User is already logged in. Redirect them somewhere useful.
if (isset($_SESSION['id']))
{
    header('Location: foo');
    exit();
}

include 'PasswordHash.php';

$sql = new mysqli('localhost', 'username', 'password', 'database_name');

$hasher = new PasswordHash(8, FALSE);

if (!empty($_POST))
{
    // Again, never trust user input!
    $username = $sql->real_escape_string($_POST['username']);

    $query = "SELECT id, password
              FROM users
              WHERE username = '{$username}'";
    $user = $sql->query($query)->fetch_object();

    /**
     * Check that the query returned a result (otherwise user doesn't exist)
     * and that provided password is correct.
     */
    if ($user && $user->password == $hasher->CheckPassword($_POST['password'], $user->password))
    {
        /**
         * Set cookies here if/as needed.
         * Set session data as needed. DO NOT store user's password in
         * cookies or sessions!
         * Redirect the user if/as required.
         */
        $_SESSION['id'] = $user->id;
    }
    /**
     * Don't provide specific details as to whether username or password was
     * incorrect. If an attacker knows they've found a valid username, you've
     * just made their life easier.
     */
    else
    {
        $error = "Login failed.";
    }
}

?>

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>User Login</title>
    </head>
    <body>
        <?php if (isset($error)): ?>
        <p class="error"><?php echo $error; ?></p>
        <?php endif; ?>

        <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">
            <fieldset id="login">
                <label for="username">Username</label>
                <input type="text" id="username" name="username" /><br />

                <label for="password">Password</label>
                <input type="password" id="password" name="password" /><br />

                <input type="submit" value="Login" />
            </fieldset>
        </form>
    </body>
</html>

Re: Login script tutorial

Posted: Mon Apr 02, 2012 5:58 pm
by social_experiment
califdon wrote:Nothing wrong with collaboration
Agreed; i am not a security specialist but if i can impart a bit of the knowledge i have picked up during my development years so be it.

Below my attempt

If you are an experienced user of php, writing a login script comes pretty easy. Not only the code part but the logic behind it. Often a php newbie (term used in the most sincere form) will attempt a login system for whatever reason. There are lots of tutorials available on the internet but the things new users tend to do is to accept these as ‘a complete and totally secure login script’ when infact they are (as authors often state) only examples which should serve as a jump off point on how to write login systems.This post also does not offer a silver bullet solution as there is none in these situations; there is only sound practise. As technology changes so may the validity techniques described here.

Because being new to this is confusing I will attempt to explain as clearly as possible. I make no claims about my knowledge on this subject, I’m adding to and combining points made by various users on the forum. Use this article as a guideline when creating a login script, always ask questions about security and if in doubt post your question on the PHPDN forum > Security section.

How the article is structured

Throughout the article I list the pitfall, give a small explanation about it and directly afterwards an alternative to the problem. If a code snippet is present it is related to the text it is located beneath. Questions, comments, improvements, things I missed, etc are all welcome.

I make the assumption that you wish to create a login system using PHP, an SQL database, have some page/s that you don’t want anyone accept a registered (and logged in) user to see. Database connections are also implied.

Pitfall 1: How do I store my password in the database?

To successfully login a user you need to do validation – Validate the credentials you get against the details you have in the database. The first step to a more secure login system is to NEVER store the password in the database, not in any form. The reason is simple: If your database is compromised, every account that you have is compromised.

The only option to consider is to store a hashed value consisting not of the password but of the password plus other 'ingredients'. How you create the hashed value is up to you, but I think one thing that most will agree on is that it shouldn’t be easy (near impossible) to determine the plain-text should the hashed value be obtained.

Many examples (and sadly some published books) advocate the use of md5() as a hashing algorithm. Md5() is not a safe hashing algorithm and shouldn’t be used for this purpose. Create a salt, create a pepper then combined these with the password; then hash with something like the sha384 algorithm (minimum). Salts can be stored in the database while peppers often reside in a file somewhere or as a string which has various characters and is at least 30 characters long.

Example of how you can create a hashed value

Code: Select all

<?php
 $hashedValue = hash('sha384', $salt.$pepper.$password);
 // returns a 96 character string which is stored in the database
?>

Pitfall 2: Escaping input

Quite a few example scripts portrays a query against the database in the following manner:

Code: Select all

<?php
$username = $_POST['username'];
$password = $_POST['password'];
$qry = "SELECT * FROM Table WHERE username='$username' and password='$password'";
?>
The $_POST values above are taken from the form; without any checking of any sort. Input should always be treated as if it is contaminated. To use data in a database query it has to be escaped. Use mysql_real_escape_string() for this purpose. mysql_real_escape_string() accepts an argument that is to be escaped making it safe to use in a database query. The explanation from the PHP Manual on the function
PHP Manual wrote: Escapes special characters in the unescaped_string , taking into account the current character set of the connection so that it is safe to place it in a mysql_query(). If binary data is to be inserted, this function must be used. mysql_real_escape_string() calls MySQL's library function mysql_real_escape_string, which prepends backslashes to the following characters: \x00, \n, \r, \, ', " and \x1a.
This function must always (with few exceptions) be used to make data safe before sending a query to MySQL.
Depending on personal requirements you should also check for empty fields, certain types of characters, certain types of data; Checking the data you receive is just as important as escaping it. Use existing php functions such as trim() or regular expressions to check input. User input is NEVER to be trusted.

Some scripts rely on the magic_quotes_gpc setting to determine whether or not to use mysql_real_escape_string(); though you could check for the existence of the value it is wise to note that from PHP 5.3.0 the feature is deprecated (and the function shouldn't be used anymore). The above code snippet can be amended as follows:

Code: Select all

<?php
// no checking of data; improve this by using existing or custom
// functions.
$username = mysql_real_escape_string($_POST['username']);
$password = mysql_real_escape_string($_POST['password']);
$qry = "SELECT * FROM Table WHERE username='$username' and password='$password'";
?>
Pitfall 3: Not regenerating a session after validation

This snippet will login a user if a match for the credentials are found but it leaves the script vulnerable to session based attacks.

Code: Select all

<?php
if($rows==1) {
header("location:/login_success.php");
}
else {
echo "Wrong Username or Password";
}
?>
Calling the session_regenerate_id() function will update the existing session id to a new value. After this is done any session values that will be used are declared. A preferred option to session_register() for registering session variables is the method below:

Code: Select all

<?php
if($rows==1) {
session_regenerate_id();
$_SESSION['username'] = $username;
$_SESSION['loggedIn'] = true;
// close the session
session_write_close();
header("location:/login_success.php");
exit();
}
else {
echo "Wrong Username or Password";
}
?>
Two types of session based attacks are session fixation and session hijacking. The following from the wikipedia pages on each type of attack
Wikipedia wrote: session fixation attacks attempt to exploit the vulnerability of a system which allows one person to fixate (set) another person's session identifier (SID).
Wikipedia wrote: session hijacking is the exploitation of a valid computer session—sometimes also called a session key—to gain unauthorized access to information or services in a computer system
session_regenerate_id() changes the existing session_id value to a different on that was issued at the start of the script; a different one to the one an attacker might have.

Pitfall 4: Checking if a visitor is logged in

I refer to code that checks if a user is logged in as 'auth' code. This code goes on each page that the user wants protected and make accessible to registered / logged in users only.

The following snippet is an example of auth code present in a first time ‘auth’ page

Code: Select all

<?php
if(!session_is_registered(myusername)) {
header("location:mainlogin.php");
}
?>
Apart from the fact that the session_is_registered() function that shouldn’t be used, this code doesn’t check the value inside the variable; from the PHP Manual
PHP Manual wrote: session_is_registered() returns TRUE if there is a global variable with the name name registered in the current session, FALSE otherwise.
Effectively myusername can be empty and the auth script would see this as "logged in". The variable is registered but a better option would be to fill it with something concrete to check against.To protect the page you need values that you can check against; a custom session id, a value in a database, a variable set on login. As with the password hashing option the amount of techniques that can be used here is abundant; personal preference comes into play so I will give a basic idea of what to implement

Code: Select all

session_start();
if (!isset($_SESSION['custom_sessid']) ||$_SESSION['custom_sessid'] == 0 || 
$_SESSION['loggedIn'] == false) {
header("location: login.php");
exit();
The snippet above checks if the session variable has not been set, if the id is equal to 0 or if the loggedId session variable is false. The key thing in here is the OR operator (||) which means that if any of the above conditions are true the browser will redirect the user atttempting to view the page.

Many options to check exists when it comes to 'auth' code but stick to values that you can set yourself; ip's and browser types can be spoofed so relying on them isn't advisable.

And that's my take on login script basics. One thing i would like to point out is the use of mysqli_ functions which should rank above mysql_ counterparts. I used the mysql_ option as it was present in the majority of the posts i searched through and i think mysqli_ will give new users a bit of an uphill battle at first. I noticed this in an earlier post by Celauran

The use of PDO is also recommended once you get the hang the login script basics and how to use prepared statements to improve interactions with the database.

Re: Login script tutorial

Posted: Mon Apr 02, 2012 8:05 pm
by califdon
I like your general approach, social_experiment. There are numerous typos that you can check later, but the only things that I saw that you might want to add or change would be:
  • Add (and emphasize) that each page that needs to read or write to session variables must have a session_start() command at the beginning of its PHP block (remember, this is aimed at newbies).
  • When discussing mysql_real_escape_string(), make it clear that this only works after the database connection has been made in a script, and only if you are using MySQL.
You and Celauran have different styles, which is fine, but it may prove a little difficult to combine parts of each. Maybe you two can PM and see if you can figure out how to do that, because you both have a lot of experience and good advice.

In any case, thanks to both of you. I know from personal experience how much effort it takes to pull together a document of this kind.

Re: Login script tutorial

Posted: Mon Apr 02, 2012 8:40 pm
by Celauran
Very different approaches, no question, but I think they're actually quite complimentary. On the one hand, you've got some boiler plate code with (hopefully) enough comments to understand the rationale behind it. Useful if you're in a hurry. On the other hand, you've got some very specific "Do this, not that. Here's why." which can be a huge boon to people looking to improve their existing code.

Re: Login script tutorial

Posted: Tue Apr 03, 2012 1:45 am
by social_experiment
@Califdon thanks for the mention of the two additional points; they are indeed things which new users neglect to look at;

@Celauran I agree; different approaches to a problem is good as it encourages different thinking and also helps with questions because it's good to say "Don't use md5()", it's to the point but leaves no alternatives - rather say "Don't use md5() but here is an alternative way of hashing".

Yes this is only a tutorial / basic how-to but if the default level of knowledge is raised somewhat it will worth the effort

Re: Login script tutorial

Posted: Fri Apr 06, 2012 3:15 am
by Riley103
Thank you so much for the post.