Basic Php Login Script

Discussion in 'Programming' started by scottlpool2003, Jan 30, 2013.

  1. #1
    Just thought I'd share this for anybody needing a login script.

    form.php
    <form method="post" action="login.php">
    <fieldset>
    <label>Username:</label> <input type="text" name="username"><br />
    <label>Password:</label> <input type="password" name="password"><br />
    <input type="submit" value="login" name="submit">
    </fieldset>
    </form>
    HTML:
    login.php
    /*
    Encrypt password to MD5
     
    Note: This is not a very secure encryption method advisable to use a more secure method
     
    */
    $encryptedpassword = md5($_POST[password]);
     
    //Connect to your database - could include it instead
     
    $db_myHost = "DB Host Name";
    $db_myUser= "Username";
    $db_myPassword = "Password";
    $db_myDatabase = "DB Name";
     
    $dbconn = new PDO('mssql:host=$db_myHost;dbname=$db_myDatabase','$db_myUser','$db_myPassword');
     
    try
      {
      $dbPDO = new PDO('mssql:host='.$db_myHost.';dbname='.$db_myDatabase, $db_myUser, $db_myPassword);
      $dbPDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
      }
    catch  (PDOException $e)
      {
        echo "Error!: " . $e->getMessage() . "
    ";
        die();
      }
     
     
    //Check user credentials
     
    $sth = $dbconn->prepare("SELECT * FROM users WHERE username = :username AND password = :password");
     
    $params = array("username" => $_POST["username"], "password" => $encryptedpassword);
    $sth->execute($params);
     
     
        while ($row = $sth->fetch()) {
          session_start();
        $_SESSION['userName'] ="$row[username]";
        header("Location:/account/");
        }
    PHP:
    EDIT:

    This is just a simple login script, please feel free to add to it and post your login scripts for users to use.
     
    Solved! View solution.
    scottlpool2003, Jan 30, 2013 IP
  2. Syndication

    Syndication Active Member

    Messages:
    351
    Likes Received:
    3
    Best Answers:
    1
    Trophy Points:
    90
    #2
    Interesting you're using PDO and MsSQL vs traditional mysql_* methods to show users a 'simple' login script, however great example involving the latest PHP5 methods of PDO.
     
    Syndication, Jan 30, 2013 IP
  3. jonasg

    jonasg Member

    Messages:
    61
    Likes Received:
    3
    Best Answers:
    0
    Trophy Points:
    43
    Digital Goods:
    1
    #3
    Nice Share! :)
     
    jonasg, Jan 30, 2013 IP
  4. scottlpool2003

    scottlpool2003 Well-Known Member

    Messages:
    1,708
    Likes Received:
    49
    Best Answers:
    9
    Trophy Points:
    150
    #4
    Yeah, for those needing to use it with MySQL simply change the mssql to mysql.
     
    scottlpool2003, Jan 31, 2013 IP
  5. shail

    shail Active Member

    Messages:
    59
    Likes Received:
    2
    Best Answers:
    0
    Trophy Points:
    95
    #5
    That's a Useful share
     
    shail, Feb 2, 2013 IP
  6. star.ruby

    star.ruby Member

    Messages:
    11
    Likes Received:
    1
    Best Answers:
    0
    Trophy Points:
    45
    #6
    Thanks for sharing! I would make a few recommendations, though.

    First of all, for somebody who needs this script, you may want to give a bit of information. I'm not as familiar with using PDO, as I use a framework with a built-in ORM; so I can't comment on this part.

    Also, md5 is not encryption. It is a way to create a hash. With the availability of rainbow tables, I would choose something with a wider range, and also include a "salt" with the password. Salting passwords helps reduce the risk that a user's password can be found out if their hash is compromised.

    However, I would add a few changes. Please feel free to comment if these are not the right idea. Also, it is late and I tried to double check, but may have missed a few things. I hope this gives you some ideas, though. :)

    form.php
    <form method="post" action="login.php">
        <fieldset>
          <label for="login_username">Username:</label> <input id="login_username" type="text" name="username"><br />
          <label for="login_password">Password:</label> <input id="login_password" type="text" name="password"><br />
          <input type="submit" value="login" name="submit">
        </fieldset>
    </form>
    HTML:
    I set the labels for the fields they are describing. This helps users that have disabilities when using some alternate browsers. Also, I changed password to a text field, since it can be a pain to make sure you are typing a bunch of different characters in a correct fashion. It is debatable, but I find password to be legacy.

    login.php
    
    // EDIT THIS!
    // Database Info:
    $dbtype = 'mysql'; // Check with your hosting provider to see what is available.
    $dbhost = 'localhost'; // usually is localhost on a shared environment
    $dbname = 'myblogsitecms'; // may also have your hosting name included if on a shared environment
    $dbusername = 'usey'; // usually has your hosting username included on a shared environment.
    $dbpassword = 'passy';
     
    // Page to go to if correct
    $redirect = "/account/";
    // Page to go to if not correct
    $bad_redirect = "/login/error";
     
    if(!isset($_POST['username'] && !isset($_POST['password']))
    {
    header("Location: /error/");
    }
     
    // The following is taken from: http://www.php.net/manual/en/pdo.connections.php
    try
    {
     
        $dbh = new PDO($dbtype.':'host='.$dbhost.';dbname='.$dbname, $dbusername, $dbpassword);
        $tryuser = $dbh->prepare("select username, userid from users where username = :username and password = :password limit 1"); // no need to select *, we just need to know that it's true--and to pull some values if it is.
        $tryuser->execute(array('username'=>$_POST['username'], 'password'=>$_POST['password']));
        if($tryuser)
        {
            $user = $tryuser->fetch(POD::FETCH_ASSOC);
     
            unset($dbh);
            unset($tryuser);
     
            session_start();
            $_SESSION['username'] = $user['username']; // I would advise using cookies, but sessions are okay, usually. However they may offer some vulnerabilities if used on a shared host.
            $_SESSION['userid'] = $user['userid'];
            header("Location: $redirect");
        }
        else
        {
            header("Location: $bad_redirect");
        }
    }
    catch (PDOException $e)
    {
        print "Error!: " . $e->getMessage() . "<br/>";
        die();
    }
    PHP:
    I am trying to keep anything related to the PDO object within the try block. Also, it is nice to save memory where possible. That's the impression I am trying to get with this code, anyway :D
     
    star.ruby, Feb 3, 2013 IP
  7. scottlpool2003

    scottlpool2003 Well-Known Member

    Messages:
    1,708
    Likes Received:
    49
    Best Answers:
    9
    Trophy Points:
    150
    #7

    Great share, this is what I like to see, Digitalpoint users joining in a discussion and adding valuable feedback.

    Your code above is a great improvement, however, you're replacing the MD5 with nothing at all. So I think this would improve it even further!


    
    // EDIT THIS!
    // Database Info:
    $dbtype = 'mysql'; // Check with your hosting provider to see what is available.
    $dbhost = 'localhost'; // usually is localhost on a shared environment
    $dbname = 'myblogsitecms'; // may also have your hosting name included if on a shared environment
    $dbusername = 'usey'; // usually has your hosting username included on a shared environment.
    $dbpassword = 'passy';
     
    // Page to go to if correct
    $redirect = "/account/";
    // Page to go to if not correct
    $bad_redirect = "/login/error";
     
    if(!isset($_POST['username'] && !isset($_POST['password']))
    {
    header("Location: /error/");
    }
     
    // The following is taken from: http://www.php.net/manual/en/pdo.connections.php
    try
    {
     
        $dbh = new PDO($dbtype.':'host='.$dbhost.';dbname='.$dbname, $dbusername, $dbpassword);
        $tryuser = $dbh->prepare("select username, userid from users where username = :username and password = :password limit 1"); // no need to select *, we just need to know that it's true--and to pull some values if it is.
     
    $hashed_password = crypt('$_POST['password']'); // let the salt be automatically generated
     
     
        $tryuser->execute(array('username'=>$_POST['username'], 'password'=>$hashed_password));
        if($tryuser)
        {
            $user = $tryuser->fetch(POD::FETCH_ASSOC);
     
            unset($dbh);
            unset($tryuser);
     
            session_start();
            $_SESSION['username'] = $user['username']; // I would advise using cookies, but sessions are okay, usually. However they may offer some vulnerabilities if used on a shared host.
            $_SESSION['userid'] = $user['userid'];
            header("Location: $redirect");
        }
        else
        {
            header("Location: $bad_redirect");
        }
    }
    catch (PDOException $e)
    {
        print "Error!: " . $e->getMessage() . "<br/>";
        die();
    }
    PHP:
     
    scottlpool2003, Feb 4, 2013 IP
  8. star.ruby

    star.ruby Member

    Messages:
    11
    Likes Received:
    1
    Best Answers:
    0
    Trophy Points:
    45
    #8
    Ah, there we go! I knew I forgot to include something ;)

    It may be my own preference, but in cases like this I find it wasteful for lines of code to create and set a whole new variable ($hashed_password) when it will only be used once.

    I think it may be more efficient to call the function and pass its value directly into where it is needed.

    So I would delete line 27, and would instead do this for line 30:
    $tryuser->execute(array('username'=>$_POST['username'], 'password'=>crypt($_POST['password'])));
    PHP:
    In cases like this, you may want to create a class with methods to deal with this, such as SECURE::crypt($password[, $salt[, SECURE::HASH_TYPE]]), where the second parameter is a salt looked up in the DB if the user is found; and the third parameter defaults to sha1 or whatever you want--but is changeable for the user. This is just a recommendation.
     
    star.ruby, Feb 4, 2013 IP
  9. scottlpool2003

    scottlpool2003 Well-Known Member

    Messages:
    1,708
    Likes Received:
    49
    Best Answers:
    9
    Trophy Points:
    150
    #9
    How would you generate the HASH though? I just created another thread asking the best practice...

    There were 2 ways that I could find, 1 to have a default salt key the other to generate new salt keys for each user.

    Would it be secure enough to generate the salt key as microtime when the user signs up and store the salt key and users password in the users table something like:

    $sth = $dbconn->prepare("SELECT password,salt FROM users WHERE username = :username ");
     
    $params = array("username" => $_POST["username"]);
    $sth->execute($params);
     
     
        while ($row = $sth->fetch()) {
        //Encrypt posted password with salt key from db
       if (hash(SHA256, $_POST[password] . microtime($salt)) !== $row[salt]){
       //passwords do not match
       }
       else {
       //passwords match
       }
     
       
        }
    PHP:
     
    scottlpool2003, Feb 4, 2013 IP
  10. scottlpool2003

    scottlpool2003 Well-Known Member

    Messages:
    1,708
    Likes Received:
    49
    Best Answers:
    9
    Trophy Points:
    150
    #10
    My post above doesn't make any sense. Never used a proper hashing method before, if anybody would like to edit the code above to securely hash the password that would be great!
     
    scottlpool2003, Feb 4, 2013 IP
  11. star.ruby

    star.ruby Member

    Messages:
    11
    Likes Received:
    1
    Best Answers:
    0
    Trophy Points:
    45
    #11
    Don't over think it too much! :D

    A salt just needs to be something added to a string; something that creates a greater range of possibilities for somebody to calculate an original password if the hash is found.

    For this, I would use random characters. When a user creates their account, create a for loop that loops 16 times, and concatenates a new character to $salt. Then write this random string to that user's row in the database when the account is made. This way you store the username, hash, and salt in the database.

    When the user logs in, you get 2 inputs from them (username, password), but will use 3 items for verification (username, password, and the salt you pull from the database).
    Rather than doing a select where username = X and password = Y, I just search for the username, first and pull the salt from there.
    Then I search for the hash where the username is the same as above, and compare the 2 salted hashes.

    It takes a tiny bit more effort, but it helps protect user credentials if there is a break-in.
     
    star.ruby, Feb 4, 2013 IP
  12. scottlpool2003

    scottlpool2003 Well-Known Member

    Messages:
    1,708
    Likes Received:
    49
    Best Answers:
    9
    Trophy Points:
    150
    #12
    Right so something along the lines of having an extra field in the db called 'salt' or 'hash' which I'm thinking is the encryption method. So user signs up, I create a microtime() and enter that as the 'salt' and convert their original password to a hashed version using the field 'salt' and query the db like so:

    $sth = $dbconn->prepare("SELECT password,salt FROM users WHERE username = :username ");
     
    $params = array("username" => $_POST[username]);
    $sth->execute($params);
     
     
        while ($row = $sth->fetch()) {
    if ($row[password] != hash(SHA256, $_POST[password] . $row[salt])){
    //Password is incorrect
    }else {
    //Password is correct
    }
    }
    PHP:
     
    scottlpool2003, Feb 4, 2013 IP
  13. star.ruby

    star.ruby Member

    Messages:
    11
    Likes Received:
    1
    Best Answers:
    0
    Trophy Points:
    45
    #13
    Your users db table should have: username, hash, salt.

    The hash is the combination of the password + salt being passed to something like sha1, md5, or any other hashing algorithm that is supported by PHP. I would not recommend any of the ones I mentioned above, as they have already had data created to try to figure out passwords/text already.

    But I'm confused why you think you need microtime. A salt is just a string of letters that is added to the password. Ideally, the salt should be a little long, and include non-alphanumeric characters.

    So for instance:
    username: scottlpool2003
    salt: jal4#2<28$);,Hz!p
    hash: md5($salt.$_POST['password']) from PHP, not the Database, where salt is prepended to the password via the period.

    Change the hash algorithm to whatever you want, md5 was an example since it's well known. Change the order of the salt and password, or create a $salt.$password.$salt sandwich. It's up to you what you want to do.
     
    star.ruby, Feb 4, 2013 IP
    scottlpool2003 likes this.
  14. Blaxus

    Blaxus Active Member

    Messages:
    23
    Likes Received:
    2
    Best Answers:
    2
    Trophy Points:
    78
    #14
    Okay.. a few things here. You all know MD5 is bad and yet you all continue to use it? There's nothing wrong with checking out alternative ways to do things. And salts are not really as strong as you think they are.

    Use the new PHP 5.5 password hashing framework. There is a legacy toolkit available that allows you to use the same code on older versions.

    I know I'm not improving on your code, but you said "Basic Login Script" and that's what i'm going to give you. Using PHP, MySQL (PDO) & the new Password Framework with Blowfish Encryption.

    I haven't tested this yet, but feel free to improve wherever.
    auth.php:
    <?php
    $db['type'] = 'mysql';
    $db['port'] = '3306';
     
    $db['host'] = 'localhost';
    $db['user'] = 'root';
    $db['pass'] = 'root';
    $db['name'] = 'testdb';
     
    $db['dsn'] = $db['type'].':dbname='.$db['name'].';host='.$db['host'];
    try
    {
        $dbh = new PDO($db['dsn'], $db['user'], $db['pass']);
    }
    catch (PDOException $e)
    {
        echo 'Connection failed: ' . $e->getMessage();
    }
     
    # Check User Authentication
    if( isset($_COOKIE['id']) && isset($_COOKIE['hash']) )
    {
        $stmt = $dbh->prepare("SELECT * FROM users WHERE id = '?' AND hash = '?'");
        $stmt->bindParam(1, $_COOKIE['id']);
        $stmt->bindParam(2, $_COOKIE['hash']);
        $stmt->execute();
     
        if( $stmt->rowCount() == 1 )
        {
            $login = true;
            $my = $stmt->fetchAll(PDO::FETCH_OBJ);
        }
    }
    PHP:
    index.php:
    <?php
    # Include Authentication
    include('auth.php');
     
    # Check if user is logged in
    if($login)
    {
        echo 'Welcome '.htmlspecialchars($my->name).'.';
    }
    else
    {
        # Redirect user to login page.
        echo '<META HTTP-EQUIV="REFRESH" CONTENT="0; URL=login.php">';
    }
    PHP:
    login.php:
    <?php
    include('auth.php');
    include('password.php');
     
    # User is not logged in
    if(!$login)
    {
        # Check to see Form submission
        if(isset($_POST['submit']))
        {
            # Select Username data
            $stmt = $dbh->prepare("SELECT * FROM users WHERE username = '?' LIMIT 1");
            # LowerCase Username, makes it less prone to error, usernames are unique anyway.
            $stmt->bindParam(1, strtolower($_POST['username']));
            $stmt->execute();
     
            $user = $stmt->fetch(PDO::FETCH_OBJ);
            $randhash = md5($user->id.time());
     
            # Verify User Password.
            if(password_verify($_POST['password'], $user->password))
            {
                setcookie("id", $user->id, time()+3600); # expire in 1 hour
                setcookie("hash", $randhash, time()+3600); # expire in 1 hour
       
                # Update User Hash
                $stmt = $dbh->prepare("UPDATE users SET hash = '?' WHERE id = '?'");
                $stmt->bindParam(1, $randhash);
                $stmt->bindParam(2, $user->id);
                $stmt->execute();
       
                echo 'Successful logged in.
                <META HTTP-EQUIV="REFRESH" CONTENT="0; URL=index.php">';
            }
        }
        else
        {
            # Example Login Form
            echo '<form method="POST" action="login.php">
            Username: <input type="text" name="username"><br />
            Password: <input type="password" name="password"><br />
            <br />
            <input type="submit" name="submit" value="Login">
            </form>';
        }
    }
    else
    {
        echo 'You are already logged in.';
    }
    PHP:
    And you can always find the latest legacy code password.php file here.
     
    Last edited: Feb 4, 2013
    Blaxus, Feb 4, 2013 IP
  15. scottlpool2003

    scottlpool2003 Well-Known Member

    Messages:
    1,708
    Likes Received:
    49
    Best Answers:
    9
    Trophy Points:
    150
    #15
    Am I missing something with the above?



    $randhash = md5($user->id.time());
    PHP:
    Wouldn't this be bad for say a forum with 200 members where the users profile page has their sign-up date publicly visibe?
     
    scottlpool2003, Feb 4, 2013 IP
  16. Blaxus

    Blaxus Active Member

    Messages:
    23
    Likes Received:
    2
    Best Answers:
    2
    Trophy Points:
    78
    #16
    Okay, you got me.... it's not good for hashing passwords (since computers became faster... google moore's law). But MD5 is fast. Which makes it perfect for creating hashes that are not passwords.

    Also, this piece of code is called when you login. Not when you sign up. So the time could be anything... even then, the time is in a number format. Good luck trying to get the exact replica of when someone logged in.

    This piece of code just needs to be random. The id along with the time create a unique random string. This needs to work like a session of sorts. It creates a unique string for a user who logs in.

    But this is just a mockup. If you want to use another piece of code that creates a random string that will work too... (or your own Session handler if you want to)

    What it is used for in this case. Is a combination system. So instead of "username & password" cookies. You make a cookie for the user's ID which you can use to retrieve user information. And a random string which is stored in the database like if it were a session.

    The combination of both allows you to be logged in across any page. So long as you include auth.php and you can use the $user variable to see if the user is logged in.
     
    Last edited: Feb 4, 2013
    Blaxus, Feb 4, 2013 IP
  17. Nf0z

    Nf0z Greenhorn

    Messages:
    19
    Likes Received:
    0
    Best Answers:
    0
    Trophy Points:
    11
    #17
    if you get compromised, hash or no hash, it will not help you.
     
    Nf0z, Feb 5, 2013 IP
  18. Blaxus

    Blaxus Active Member

    Messages:
    23
    Likes Received:
    2
    Best Answers:
    2
    Trophy Points:
    78
    #18
    Funny you should say that. Because that's true. You can increase your chances like change your password every 5 minutes. But you know as well as I do that no one likes doing that, so it all boils down to how hard you want to make the protection really.

    There are things you can do to improve your odds. But as NfOz elegantly pointed out. On the off chance that user data becomes compromised (you don't always have control over that), none of this will help you.
     
    Blaxus, Feb 5, 2013 IP
  19. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,999
    Best Answers:
    253
    Trophy Points:
    515
    #19
    1) Nice to see PDO -- it's annoying how so many people are STILL using the deprecated insecure and generally all around bad mysql_ functions since there's so many web-rot tutorials out there.

    2) kinda nice to see a well formed form -- but it would be nicer it was EITHER HTML or XHTML, and not a mix of both -- and if those labels had a FOR attribute pointing at the associated ID's... :D

    <form method="post" action="login.php">
    	<fieldset>
    
    		<label for="loginUsername">Username:</label>
    		<input type="text" name="username" id="loginUsername" />
    		<br />
    
    		<label for="loginPassword">Password:</label>
    		<input type="password" name="password" id="loginPassword" />
    		<br />
    
    		<input type="submit" value="login" name="submit" />
    
    	</fieldset>
    </form>
    Code (markup):
    3) as others mentioned, MD5's kinda crappy by modern standards -- I'd probably be using SHA512 or any of the other more complex ones offered by the hash function. As hash goes MD5 is mersh, sha512 is Maui Wowie, and Whirlpool is Alaskan Thunderfu...

    Seriously, single pass 128 bit that there are known cracks for? Thanks but no.

    I trush SHA512, but I'm really starting to dig whirlpool -- even if both can take a while.

    4) when working with PDO I usually store the full DSN string, but that's because on local testing and some windows setups you need to use sockets instead of hosts.

    5) Not sure why you'd waste an extra variable possibly putting it into the global space on the hashed version of the password -- I'd just do that on the exec line.

    6) I would NOT return password as part of the query check -- at most I'd select ID, name -- there is NO reason to be returning the Md5; you've got the where, let it handle it.

    7) While? Should only be one result, do an IF instead since if that while actually loops... well, that would mean something is wrong with the database.

    8) Why the blazes would you be processing the username index inside double quotes?!?

    9) Put the login in a function or class instead of pulling "stupid redirect tricks" with the headers.

    Just saying.
     
    deathshadow, Feb 5, 2013 IP
    scottlpool2003 likes this.
  20. scottlpool2003

    scottlpool2003 Well-Known Member

    Messages:
    1,708
    Likes Received:
    49
    Best Answers:
    9
    Trophy Points:
    150
    #20

    Some great advise there. Appreciated as always.

    I've done a lot of learning over the last few months honing in on basic skills and moving up with times from PHP4 to PHP5 and I'm getting there slowly.

    I've been trying to learn classes for ages but its one of those things, the more I look at it and try to do it, the harder it becomes.

    The tutorials are just not set out in a way that I learn... blueprints for houses, cars I just don't get it. I need to learn in ways that I do.

    How would I go about putting the above login script into a class?
     
    scottlpool2003, Feb 6, 2013 IP