...

View Full Version : Secure PHP Login - I Need a How-To for a Beginner



Rowsdower!
03-27-2009, 10:21 PM
I just got my first account with PHP/MySQL access so I am just starting my voyage into this new world. My usual method to learning anything is to get in over my head and try to struggle into understanding. It usually works pretty well but I need working examples to play with. The PHP/MySQL process has been no different so here's what I'm trying to figure out...

I first got my feet wet with basic syntax for echos, arrays, etc. Then I moved on to a VERY basic login structure (using this tutorial: http://www.phpeasystep.com/workshopview.php?id=6). I made my very first MySQL databse successfully and got the PHP to work with it. Existing logins were accepted and others were given a failed login notice. So far so good. The HTML doesn't validate but that's just because it's a quick and dirty page to test PHP functionality. (http://rowsdower.freehostia.com/ user name "test" password "test")

The wimpy, basic version I have implemented does not use sessions/cookies and does not check to see if a user is logged in before showing the content of the destination page. Part of it looks like it's supposed to, but there are no provisions to actually start a session that I can see. I would guess that this would require another row in the database to store whether or not a session is in progress or else to store some type of cookie data. Is this correct?

Anyway, as a result of the weakness in this setup anyone can navigate directly to the URL if they are able to guess at it.

The basic structure and flow from document to document during the login procedure is index.php --> checklogin.php --> login_success.php
Here is the code I have used in each case...

index.php:
<!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" xml:lang="en" lang="en">
<head>
<title>My First PHP :)</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
</head>
<body>
<div>
<span>The Junior method:</span>
<table width="300" border="0" align="center" cellpadding="0" cellspacing="1" bgcolor="#CCCCCC">
<tr>
<form name="form1" method="post" action="checklogin.php">
<td>
<table width="100%" border="0" cellpadding="3" cellspacing="1" bgcolor="#FFFFFF">
<tr>
<td colspan="3"><strong>Member Login </strong></td>
</tr>
<tr>
<td width="78">Username</td>
<td width="6">:</td>
<td width="294"><input name="myusername" type="text" id="myusername"></td>
</tr>
<tr>
<td>Password</td>
<td>:</td>
<td><input name="mypassword" type="password" id="mypassword"></td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td><input type="submit" name="Submit" value="Login"></td>
</tr>
</table>
</td>
</form>
</tr>
</table>


</div>
</body>
</html>

checklogin.php:
<?php
$host="mysql4.freehostia.com"; // Host name
$username="<whatever>"; // Mysql username
$password="<whatever>"; // Mysql password
$db_name="<whatever>"; // Database name
$tbl_name="<whatever>"; // Table name

// Connect to server and select databse.
mysql_connect("$host", "$username", "$password")or die("No successful login to MySQL account.");
mysql_select_db("$db_name")or die("cannot select DB");

// username and password sent from form
$myusername=$_POST['myusername'];
$mypassword=$_POST['mypassword'];

// ADDED FOR ENCRYPTION
// encrypt password
$encrypted_mypassword=md5($mypassword);

// To protect MySQL injection (more detail about MySQL injection)
$myusername = stripslashes($myusername);
$mypassword = stripslashes($mypassword);
$myusername = mysql_real_escape_string($myusername);
$mypassword = mysql_real_escape_string($mypassword);

//$sql="SELECT * FROM $tbl_name WHERE username='$myusername' and password='$mypassword'";
//$result=mysql_query($sql);

//ADDED FOR ENCRYPTION - revert to form above if not desired
$sql="SELECT * FROM $tbl_name WHERE username='$myusername' and password='$encrypted_mypassword'";
$result=mysql_query($sql);

// Mysql_num_row is counting table row
$count=mysql_num_rows($result);
// If result matched $myusername and $mypassword, table row must be 1 row

if($count==1){
// Register $myusername, $mypassword and redirect to file "login_success.php"
session_register("myusername");
session_register("encrypted_mypassword");
header("location:login_success.php");
}
else {
echo "Wrong Username or Password";
}
?>

login_success.php:
<?
// Check if session is not registered , redirect back to main page.
// Put this code in first line of web page.
session_start();
if(!session_is_registered(myusername)){
header("location:index.php");
}
?>

<html>
<body>
Login Successful
</body>
</html>


What I want to do now is figure out the overall structure of a more mature/sophisticated PHP/MySQL login system so that subsequent pages are not viewable unless the user is actually logged in. I found an article here (http://www.devshed.com/c/a/PHP/Creating-a-Secure-PHP-Login-Script/) detailing the code needed for a secure PHP login. The problem is that I don't know what file to put each chunk of code in. I can take a semi-educated guess for some, but for others I have no clue.

This chunk, for instance, I would guess goes into a PHP file (like my checklogin.php above and NOT in the index page) to check the login credentials:

function &db_connect() {
require_once 'DB.php';
PEAR::setErrorHandling(PEAR_ERROR_DIE);
$db_host = 'localhost';
$db_user = 'shaggy';
$db_pass = 'password';
$db_name = 'shaggy';
$dsn = "mysql://$db_user:$db_pass@unix+$db_host/$db_name";
$db = DB::connect($dsn);
$db->setFetchMode(DB_FETCHMODE_OBJECT);
return $db;
}

But am I right about this? I have no clue.

And this part:

function session_defaults() {
$_SESSION['logged'] = false;
$_SESSION['uid'] = 0;
$_SESSION['username'] = '';
$_SESSION['cookie'] = 0;
$_SESSION['remember'] = false;
}

if (!isset($_SESSION['uid']) ) {
session_defaults();
}

Would this go in each secure page, or would this just go in the same checklogin.php file as the above?

These three seem to pretty obviously go into a checklogin.php type of file:
function _checkLogin($username, $password, $remember) {
$username = $this->db->quote($username);
$password = $this->db->quote(md5($password));
$sql = "SELECT * FROM member WHERE " .
"username = $username AND " .
"password = $password";
$result = $this->db->getRow($sql);
if ( is_object($result) ) {
$this->_setSession($result, $remember);
return true;
} else {
$this->failed = true;
$this->_logout();
return false;
}
}




function _setSession(&$values, $remember, $init = true) {
$this->id = $values->id;
$_SESSION['uid'] = $this->id;
$_SESSION['username'] = htmlspecialchars($values->username);
$_SESSION['cookie'] = $values->cookie;
$_SESSION['logged'] = true;
if ($remember) {
$this->updateCookie($values->cookie, true);
}
if ($init) {
$session = $this->db->quote(session_id());
$ip = $this->db->quote($_SERVER['REMOTE_ADDR']);

$sql = "UPDATE member SET session = $session, ip = $ip WHERE " .
"id = $this->id";
$this->db->query($sql);
}
}



function updateCookie($cookie, $save) {
$_SESSION['cookie'] = $cookie;
if ($save) {
$cookie = serialize(array($_SESSION['username'], $cookie) );
set_cookie('mtwebLogin', $cookie, time() + 31104000, '/directory/');
}
}

And these two would pretty obviously go into each secured page:
function _checkRemembered($cookie) {
list($username, $cookie) = @unserialize($cookie);
if (!$username or !$cookie) return;
$username = $this->db->quote($username);
$cookie = $this->db->quote($cookie);
$sql = "SELECT * FROM member WHERE " .
"(username = $username) AND (cookie = $cookie)";
$result = $this->db->getRow($sql);
if (is_object($result) ) {
$this->_setSession($result, true);
}
}


function _checkSession() {
$username = $this->db->quote($_SESSION['username']);
$cookie = $this->db->quote($_SESSION['cookie']);
$session = $this->db->quote(session_id());
$ip = $this->db->quote($_SERVER['REMOTE_ADDR']);
$sql = "SELECT * FROM member WHERE " .
"(username = $username) AND (cookie = $cookie) AND " .
"(session = $session) AND (ip = $ip)";
$result = $this->db->getRow($sql);
if (is_object($result) ) {
$this->_setSession($result, false, false);
} else {
$this->_logout();
}
}

So how much of this do I have wrong so far? :D


If this were HTML or CSS I would just look at other people's work and dissect it until I figure out how it works. In this case though, it's not viewable and I have no idea where to begin putting these code snippets.

If I can get one sample working I can start picking it apart but until then I am out of my element. Can anyone help me sort this out or give me a dummy setup (zip) of a semi-sophisiticated login setup that they use so I can study up on it?

Also, is the md5 hash really as effective for encrypting passwords as I keep reading it is or should I be looking at different possibilities?

So many questions for a hungry mind!

sea4me
03-28-2009, 12:03 AM
Well I recommend you to use:
http://evolt.org/node/60384

It explains what each file does and you'll learn from it....

That was my script that I played with and then more deeply into PHP :D

abduraooft
03-28-2009, 10:44 AM
// To protect MySQL injection (more detail about MySQL injection)
$myusername = stripslashes($myusername);
$mypassword = stripslashes($mypassword);
$myusername = mysql_real_escape_string($myusername);
$mypassword = mysql_real_escape_string($mypassword);
You don't need to strip the slashes, unless magic_quote_gpc is enabled, I'd recommend you to read http://www.codingforums.com/showthread.php?t=144149



session_register("myusername");
session_register("encrypted_mypassword");
session_register() (http://php.net/session_register) is deprecated, always check the manual for latest updates.

Rowsdower!
03-28-2009, 11:22 PM
Well I recommend you to use:
http://evolt.org/node/60384

It explains what each file does and you'll learn from it....

That was my script that I played with and then more deeply into PHP :D

Ooh, this looks promising. Thanks!

@ abduraooft, I guess I got a bad tutorial before. Anyway, my first emphasis is on understanding the nuts and bolts before I work on the polishing of the code. It's probably not the most efficient way, but it's just how I like to learn. Manual reading will come further on down the road. I appreciate the input, though. I'll be checking up on the strip slash/magic quotes issue to make sure I don't get funky data in the database.

PappaJohn
03-29-2009, 12:12 AM
For Persistent Logins (aka 'Remember Me'), here is a very good article: http://jaspan.com/improved_persistent_login_cookie_best_practice

Rowsdower!
03-29-2009, 12:50 AM
I read something similar that suggested adding an IP address field check (IP address updated on the DB with each login, I suppose) to the session validation as well for even better security for the user's login. Is there any merit to this or would the dynamic IP addresses of users (where applicable) be an issue?



EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum