Web Design and Programming Pt 21 Secure Login Script

Help Web Design and ProgrammingIn this video tutorial I create an extremely secure PHP Login Script. If you didn’t watch part 20 of this tutorial, you should check it out to find out how to use PHP Sessions and Cookies.

To make this login secure I added all of the following security features:

  • Keep all user information stored on the server with Sessions
  • Run all input through very strict Regular Expressions
  • Use a CAPTCHA system to avoid brute force attacks (How to Setup Captcha)
  • Pass password information in encrypted form
  • Create a second token that changes on every page
  • Regenerate a new session token every time the user updates sensitive information

I add a few additional tricks along the way. All of the code follows the video. If you have any ideas on how I could improve this script pass them along.

Code From the Video

Goodlogin.php Script
<?php
// Initialize a session.
session_start();
require_once(“./includes/confighamdb.php”);
?>
<?php
if (isset($_POST['submitted'])) { // Check if the form has been submitted.
if (preg_match (‘%^[A-Za-z0-9]\S{8,20}$%’, stripslashes(trim($_POST['userid'])))) {
$u = escape_data($_POST['userid']);
} else {
$u = FALSE;
echo ‘<p><font color=”red” size=”+1″>Please enter a valid User ID!</font></p>’;
}
// FIX IT Check for a good password
if (preg_match (‘%^[A-Za-z0-9]\S{8,20}$%’, stripslashes(trim($_POST['pass'])))) {
$p = escape_data($_POST['pass']);
} else {
$p = FALSE;
echo ‘<p><font color=”red” size=”+1″>Please enter a valid Password!</font></p>’;
}
// FIX IT PHP Code for the CAPTCHA System
$captchchk = 1;
require_once(‘./includes/recaptchalib.php’);
$privatekey = “privatekey”;
$resp = recaptcha_check_answer ($privatekey,
$_SERVER["REMOTE_ADDR"],
$_POST["recaptcha_challenge_field"],
$_POST["recaptcha_response_field"]);
if (!$resp->is_valid) {
// What happens when the CAPTCHA was entered incorrectly
echo ‘<p><font color=”red” size=”+1″>The CAPTCHA Code wasn\’t entered correctly!</font></p>’;
$captchchk = 0;
}
// Query the database.
if ($u && $p && $captchchk) {
$query = “SELECT user_id, first_name, last_name, email, userid, pass FROM users WHERE userid=’$u’ AND pass=SHA(‘$p’)”;
$result = mysql_query ($query) or trigger_error(“Either the Userid or Password are incorrect”);
if (mysql_affected_rows() == 1) { // A match was made.
$row = mysql_fetch_array ($result, MYSQL_NUM);
mysql_free_result($result);
$_SESSION['first_name'] = $row[1];
$_SESSION['userid'] = $row[4];
// Create Second Token
$tokenId = rand(10000, 9999999);
$query2 = “update users set tokenid = $tokenId where userid = ‘$_SESSION[userid]‘”;
$result2 = mysql_query ($query2);
$_SESSION['token_id'] = $tokenId;
session_regenerate_id();
header(“Location: http://localhost/login/index.php”);
mysql_close(); // Close the database connection.
exit();
}
} else { // No match was made.
echo ‘<br><br><p><font color=”red” size=”+1″>Either the Userid or Password are incorrect</font></p>’;
mysql_close(); // Close the database connection
exit();
}
echo ‘<br><br><p><font color=”red” size=”+1″>Either the Userid or Password are incorrect</font></p>’;
mysql_close(); // Close the database connection
exit();
}
// End of SUBMIT conditional.
?>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” lang=”en” xml:lang=”en”>
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=ISO-8859-1″ />
<title>Good Login</title>
</head>
<body>
<div id=”main”>
<?php
echo ‘<h1>Welcome’;
if (isset($_SESSION['first_name'])) {
echo “, {$_SESSION['first_name']}!”;
}
echo ‘</h1>’;
// Display links based upon the login status
if (isset($_SESSION['userid']) AND (substr($_SERVER['PHP_SELF'], -10) != ‘logout.php’)) {
echo ‘<a href=”logout.php”>Logout</a><br />
<a href=”change_password.php”>Change Password</a><br />’;
} else { //  Not logged in.
echo ‘ <a href=”register.php”>Register</a><br />
<a href=”goodlogin.php”>Login to your account</a><br />
<a href=”forgot_password.php”>Forgot Password</a><br />’;
}
?>
<h1>Login</h1>
<p>Your browser must allow cookies in order to log in.</p>
<form action=”goodlogin.php” method=”post”>
<fieldset>
<p><b>Userid:</b> <input type=”text” name=”userid” size=”20″ maxlength=”20″ /></p>
<p><b>Password:</b> <input type=”password” name=”pass” size=”20″ maxlength=”20″ /></p>
<?php
require_once(‘./includes/recaptchalib.php’);
$publickey = “publickey”; // you got this from the signup page
echo recaptcha_get_html($publickey);
?>
<div align=”center”><input type=”submit” name=”submit” value=”Login” /></div>
<input type=”hidden” name=”submitted” value=”TRUE” />
</fieldset>
</form>
</div>
</body>
</html>

Confighamdb.php Script

<?php

// Define these as constants so that they can’t be changed

DEFINE (‘DBUSER’, ‘mysqladm’);

DEFINE (‘DBPW’, ‘password’);

DEFINE (‘DBHOST’, ‘localhost’);

DEFINE (‘DBNAME’, ‘hamdb’);

if ($dbc = mysql_connect (DBHOST, DBUSER, DBPW)) {

if (!mysql_select_db (DBNAME)) { // If it can’t select the database.

// Handle the error.

trigger_error(“Could not select the database!<br />MySQL Error: ” . mysql_error());

exit();

} // End of mysql_select_db IF.

} else {

// Print a message to the user, and kill the script.

trigger_error(“Could not connect to MySQL!<br />MySQL Error: ” . mysql_error());

exit();

}

// A function that strips harmful data.

function escape_data ($data) {

// Check for mysql_real_escape_string() support.

// This function escapes characters that could be used for sql injection

if (function_exists(‘mysql_real_escape_string’)) {

global $dbc; // Need the connection.

$data = mysql_real_escape_string (trim($data), $dbc);

$data = strip_tags($data);

} else {

$data = mysql_escape_string (trim($data));

$data = strip_tags($data);

}

// Return the escaped value.

return $data;

} // End of function.

?>

42 Responses to “Web Design and Programming Pt 21 Secure Login Script”

  1. Thanks for all this information.

  2. Person says:

    Thank you for this! How do we get it on our website? I am currently making a site on Weebly. I put a custom HTML box in it and tried copy/pasting your script in but it isn’t showing up right. Can you help me? Thanks! :)

    • admin says:

      Sorry to create a secure login system you need to have a database. As far as I can tell weebly doesn’t provide database access or creation.

  3. Bill says:

    I am working with the godlogin.php file. I have it working up to this point:

    $query = “SELECT user_id, first_name, last_name, email, pass FROM users_tbl WHERE user_id=’$u’ AND pass=SHA(‘$p’)”;

    I feel this is correct since the next line:

    $result = mysql_query ($query) or trigger_error(“Either the Userid or Password are incorrect”);

    Does not trigger the error.

    I have used register2.php from the root of the zip file to enter the data into the database. Everything seems to be there.

    Any suggestions on what I should try?

    • admin says:

      What I always do when I get an error is to print out to screen all of the values for each variable. I then check to make sure they have the correct values.

      Since you copied the code though I’m guessing there is a problem with the database. Check to make sure every value is of the correct type in the database.

      For the users I have the database set up this way

      user_id | mediumint(8) unsigned | NO | PRI | NULL | auto_increment |
      | lang_id | tinyint(3) unsigned | NO | | NULL | |
      | time_zone | varchar(30) | NO | | NULL | |
      | username | varchar(30) | NO | UNI | NULL | |
      | pass | char(40) | NO | | NULL | |
      | email | varchar(60) | NO | UNI | NULL |

      I hope that helps?

  4. Bill says:

    When I run the goodlogin.php should I be able to look in the database and see something in the token row?

  5. Bill says:

    I found my issue. I thought it was a good idea to limit the password to 20 characters. Then I realized that SHA requires 40 characters.

    I will work some more on this.

  6. Jashid says:

    Great tutorial…
    I appreciate the effort you take to made such a good tutorial

    BUT

    After the login button pressed,the reCaptcha system returns an error says “Could not open socket”.I tried many methods posted in different blogs.But none of them works for me.Why did this happens?

    • admin says:

      It seems that there is a problem with the captcha system with some fedora browsers. Here is the fix

      Replace
      define(“RECAPTCHA_VERIFY_SERVER”, “api-verify.recaptcha.net”);

      With this
      define(“RECAPTCHA_VERIFY_SERVER”, gethostbyname(‘api-verify.recaptcha.net’));

      • Jashid says:

        In my recaptcha file,it’s like,

        define(“RECAPTCHA_API_SERVER”, “http://www.google.com/recaptcha/api”);
        define(“RECAPTCHA_API_SECURE_SERVER”, “https://www.google.com/recaptcha/api”);
        define(“RECAPTCHA_VERIFY_SERVER”, “www.google.com”);

        So,i changed last line in to

        define(“RECAPTCHA_VERIFY_SERVER”, gethostbyname(‘www.google.com’));

        But

        Same Error……

        • admin says:

          That’s strange. I don’t have the problem with my code even without the change on any browser. There must be a typo in your code somewhere. Did you download the code from my site or did you copy it from the page?

  7. Jade says:

    I have not yet added this to my website, but it is the best one that I have found on the internet. I need a login system that will only allow logged in users to view all areas of my website.
    will this system work?

  8. Faizan Mustafa says:

    First of All, Thankyou for all these great tutorials.

    I have been following your videos for quiet a time now. I must admit that i learned jquery, wordpress, html, css and almost everything out there by watching your videos.

    Keep up the good work. Best Wishes for future

    • admin says:

      I’m very happy that the tutorials helped you :) It was always my dream to be able to provide a free education to all that wished to take the time to learn.

      I have many more tutorials planned. I’ll finish up programming and then move on to electronics, mathematics, physics, etc.

      Thanks for taking the time to show your appreciation

  9. Paul Mc says:

    $query = “SELECT user_id, first_name, last_name, email, userid, pass FROM users WHERE userid=’$u’ AND pass=SHA(‘$p’)”;
    $result = mysql_query ($query) or trigger_error(“Either the Userid or Password are incorrect”);
    if (mysql_affected_rows() == 1) { // A match was made….

    After a SELECT shouldn’t you be using


    if (mysql_num_rows() == 1)

    On my server mysql_affected_rows() always returns zero after a SELECT.

  10. Muhammad Ashar says:

    I have been looking websites on Google.com for website security tips and found your website and found your secure user login tutorial. can you guide me how to check vulnerabilities in my website and how i can over come them. is there any plan for cover with taking some example website and show step by step how to secure the site.

    You are very good and i hope you consider my request too.

    Keep up the good work. Best Wishes for future

    • admin says:

      Take a look at my PHP Security tutorials. I cover pretty much everything in them. All together I cover
      SQL Injection
      Cross Site Scripting (XSS)
      Malicious Code Encoding
      Session Hijacking
      Session Fixation
      Malicious System Calls
      Buffer Overflows

  11. Jr says:

    Hey there!
    First of all, you have really nice tutorials.

    And I would like to know how the code would be if I wanted to log in with an email instead of a username.
    I get confused where it says userid. I don’t know if it’s the username(userid) in some parts or the indexed user id(user_id) so I’m not sure which ones to change.

    I hope it’s clear.

    Thanks!

    • admin says:

      You can use either a userid or an email address. All you’ll need to change is to replace the useid regular expression test with the already provided email regular expression test. Does that answer your question?

  12. Joseph says:

    Thanks for taking the time to put this comprehensive working tut together. It was immensely helpful for me get all my own thoughts to crystallize & to have a framework concept to start with. I would only add the following to your superb design: in searching for secure login ideas I have also come across using a salt with the hash for certain security vectors. Here is a good how to: http://crackstation.net/hashing-security.html It integrated easily enough with all the concepts you put together. sha256 is now available & I used it for the hashing. Another resource that might be good for all of us to use is OWASP (Open Web Application Security Project) site for php: http://code.google.com/p/owasp-esapi-php/
    The OWASP home is https://www.owasp.org/index.php/Main_Page

    • admin says:

      I’m glad ou liked it. Thanks for pointing out all of the additional resources. I should update tutorials, but after I finish them I don’t often go back

  13. Murrad says:

    I am adding a portion to our website and they have asked if we can create a login that allows our employees to login and use it like a base-camp, not sure if you know what that is but it allows use to keep track of projects.

  14. Michael says:

    Hey,

    First of all, thanks for these great tutorials, they are very useful. I’ve given them a load of thumbs up on youtube, keep up the good work.

    I have coded up the login scripts and it’s all working great until the the point where I reach the index.php page. Then nothing happens – I don’t get the options to logout etc (or anything in fact). Should my index.php file have anything in it? Or should it be written to by the login.php file as appears to happen in your video?

    I might be missing something here, but it seems like most of the php scripting part of login.php should be hidden away in a private folder. Is that correct?

    Any hints would be much appreciated. Thanks,

    Michael

  15. Michael says:

    Hey, I’ve sorted the index.php script, I should have been more patient, it was all in the next video!

    Unfortunately I have a new problem, my header $_SESSION['token_id'] doesn’t seem to survive the transfer to the new index.php page once the user has logged in. Any hints as to why would be much appreciated, thanks!

  16. Michael says:

    * EDIT: I meant $_SESSION['first_name'] and $_SESSION['userid']

    Thanks!

  17. Baazil says:

    hello, sir i want to start a new garments business retail shop of garments so am looking for a very outstanding logo….. normally on net thre was a basic logo of garments will u give a best one ??

  18. imam wahyudi says:

    Dear Derek Banas,

    Thanks for your great tutorials. But I have some questions
    regarding “passing password information in encrypted form”.

    Usually, in login system, we use two files to handle that.
    First is index.php (that contains the form),
    and the second is the php file that collect & process variables
    from the index.php (, which is goodlogin.php, in your tutorial).

    In the form (index.php), password is stored in the “pass” variable.
    And it is SENT to the server, and then processed by the goodlogin.php
    by the $_POST method.

    We see that the password is sent to the server BEFORE it is encrypted.
    And someone could steal the $pass variable.
    So I think I haven’t seen your method in “passing password information
    in encrypted form”.

    Can you give me the way in doing that ?
    Since I would like to encrypt the password before it was sent to server.

    Thanks before.
    Regards,
    Imam Wahyudi.

    • Derek Banas says:

      Thank you Imam :) I use the Zend framework for authentication in a login system, but I haven’t had time to cover that topic yet. Here is a pretty good tutorial on how that is done. I’ll do my best to cover Zend as soon as possible

  19. Afi says:

    Can you please help me here, this is not working for me..

    Why does here the action is referring to the same page?

    Your tutorials are great. I have learned alot from you.
    Thanking you in advance.

  20. Ajay says:

    Hi Derek,

    I have gone through the secure login tutorial and understood most of it except one small bit that is regarding the tokenID, specifically the line

    $tokenId = rand(10000, 9999999);

    whose value you update into the DB & you call this as the second layer of security. I am unable to understand how this tokenID is helping in security? I mean I don’t see where this value is being called up and checked for any identification.
    Could you please explain its relevance by pointing out its usage in the program.

    Thanks for the overall wonderful Tutorial.

    • Derek Banas says:

      Hi Ajay,

      You’re very welcome :) I’m glad you found them useful. I continue writing that program in the tutorials that follow. Sorry that I didn’t make that clear. I hope to update this information soon when I cover the Zend framework which is what I have been using for the past couple years.

  21. Jonas says:

    Also, in te forgotpassword file, how can i chance the user id to Email, and The Mother or Grandmother maide name to someting like Usernam (i have customized the Register script to have username insted of “Zip, state, city”)

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Google+