Jump to content

basic login tutorial


killboy

Recommended Posts

Well, I was looking kishou's "Creating a full login and register page tutorial" topic, and I just decided to make my own login tutorial, using the Object-Oriented Programming and inspire others to do it as well.OK, let's get to business:First, I'm gonna create 3 different classes:DataBase, User, AdminUser; where:* DataBase has all the connection parameters and all the basic functions; this way if we're using MySQL (like in this case) and we have to migrate to a different database manager, we'll only have to change the class methods.* User contains the basic information of a user; in this case, username and password.* AdminUser will perform all the interaction within the user and the database; in this case, will only allow us to login.DataBase class:

<?phpclass DataBase{	var $connection;	function DataBase($server,$username,$password,$db_name)	{		$this->connect_db($server,$username,$password,$db_name);	}	//	private function connect_db($server,$username,$password,$db_name)	{		$this->connection=mysql_connect($server,$username,$password) 					or die("COULD NOT CONNECT TO THE DATABASE ");		mysql_select_db($db_name,$this->connection);	}	//	public function exec_query($sql_query)	{		return mysql_query($sql_query,$this->connection);	}	//	public function show_result($query_result)	{		return mysql_fetch_object($query_result);	}	//	public function getRows($query_result)	{		return mysql_num_rows($result);	}	//	public function close_connection()	{		mysql_close($this->connection);	}}?>

User class:

<?phpclass User{	var $username,$password;		function User($username,$password)	{		$this->setUserName($username);		$this->setPassword($password);	}	//	public function setUserName($username)	{		$this->username=$username;	}	//	public function setPassword($password)	{		$this->password=$password;	}	//	public function getUserName()	{		return $this->username;	}	//	public function getPassword()	{		return $this->password;	}}?>

AdminUser class:

<?phpclass AdminUser{	var $database;		function AdminUser($database)	{		$this->database=$database;	}	//	public function login($user)	{		$username=mysql_real_escape_string($user->getUserName());		$password=md5($user->getPassword());				$query="select * from user where username='$username' and password='$password'";		$result=$this->database->exec_query($query);		if ($this->database->getRows($result)==0)			return;		return $user;	}}?>

Every class is a different .php file so I can know what every file does without having to open them directly. These files will be stored on a new directory called "classes".Now I'll create the index:Note: Ignore the non-XHTML standards.

<?phpinclude ("classes/user.php");session_start();?><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><title>Index</title></head><body><?if (!isset($_SESSION['user']) || $_SESSION['user']==null){?>	<center>LOGIN</center>	<br>	<table align="center">	<form action="login.php" method="post">	<tr><td>Username</td><td><input type="text" name="username"></td></tr>	<tr><td>Password</td><td><input type="password" name="password"></td></tr>	<tr align="center"><td colspan="2"><input type="submit" value="Login"> <input type="reset" value="Reset Fields"></td></tr>	</form>	</table><?}else	echo "Welcome ".$_SESSION['user']->getUserName();?></body></html>

As you can see, I'm using sessions as well. sessions can be also objects.In this case, $_SESSION['user'] will be an object of the type User, therefore, it will has its methods.If you see the form directs me to a page called login.php, will be the validating page here and will put some value on the session variable.login.php:

<?phpinclude ("classes/bd.php");include ("classes/user.php");include ("classes/admin_user.php");session_start();$username=$_POST['username'];$password=$_POST['password'];$new_user=new User($username,$password);$database=new DataBase("server","db_username","db_password","db_name");$admin_user=new AdminUser ($database);$_SESSION['user']=$admin_user->login($new_user);$database->close_connection();echo "Validating username and password...<meta http-equiv=\"refresh\" content=\"3;URL=index.php\">";?>

In this page is where we make use of the different objects methods. As you can see, if you have to do some modification later on, you'll only have to do them in the objects, none of this previous code will have to be touched.Okay, that was it.I hope it helps, any comment / suggestion / question will be received.Whatever you don't understand about the script, feel free to ask.

Link to comment
Share on other sites

I'm sorry to say this, but your script suffers from the same security issues which kishou's script initially had. In particular, you're vulnerable to SQL injections, since you never escape the $_POST['username'] or $_POST['password'] fields. I believe you may also be vulnerable at other spots (something with the sessions just feels wrong), but honestly, I can't spot them because of all those methods.As far as DB portability goes, I'd reccomend PEAR's MDB2 every day over custom scripts.Where exactly is the registration page anyway?

Link to comment
Share on other sites

I'm sorry to say this, but your script suffers from the same security issues which kishou's script initially had. In particular, you're vulnerable to SQL injections, since you never escape the $_POST['username'] or $_POST['password'] fields. I believe you may also be vulnerable at other spots (something with the sessions just feels wrong), but honestly, I can't spot them because of all those methods.As far as DB portability goes, I'd reccomend PEAR's MDB2 every day over custom scripts.Where exactly is the registration page anyway?
Okay then:1- $user->getUserName() returns the username within the addslashes function. $admin_user->login() receives an object as a parameter; just check it. I must admit though that I didn't escaped the password.2- There's no registration page, it's basic login tutorial; I assume registration is done.3- The main aim of this was basically to promote more order in people's future scripts, and let them know how useful OOP is in order to do this.Thanks for the suggestions though.If you or any other user has the time to point out the security holes, I'd be really thankful. This is a very common script for me and I use it all the time.
Link to comment
Share on other sites

Well, basically, instead of just adding slashes, you need to do a mysql_real_escape_string() on it. This will guarantee that the input will be treated as a literal string regardless of it's contents.As Synook said already, it would be nice if you hash passwords too.

Link to comment
Share on other sites

Okay, but hash the password is not helpful for me. Suppose your doing a forums and some user lost his password and want it back. How can I get the hashed password back to its original characters?The only I know is base64_encode() function, wich also has base64_decode(); is there any other choices?

Link to comment
Share on other sites

Okay, but hash the password is not helpful for me. Suppose your doing a forums and some user lost his password and want it back. How can I get the hashed password back to its original characters?The only I know is base64_encode() function, wich also has base64_decode(); is there any other choices?
You'll generate a new password for the user, after which they can login with it, and change their password to whatever they like. The point of hashing is never to show the original password in plain text.
Link to comment
Share on other sites

  • 2 weeks later...

I made this Database class

<?php class db{	 var $lastQuery;	 //the last query	 var $currentDb;	 //The database we are working in.	 var $params=array();	 //Paramters	 function __construct($host,$user,$pass,$db){		 $this->msa = mysql_connect($host,$user,$pass) or die(mysql_error());		 @mysql_select_db($db) or die(mysql_error());		 $this->dbSelected = $db;		 //For use of the "run_on_db function"	 }	 	 //Used to set the params. $keys can be either a string or an associated array. $args only needs to be present if	 // $keys is a string	 	 function setParams($keys,$args=false){		 if($args===false AND is_array($keys)){			 $this->params+= $keys;		 }elseif(is_string($keys) AND $args!==false){			 $this->params[$keys] = $args;		 }	 }	 	 /*	   mixed query(SQL $sql[, int $mode],[array $params])	   		Takes a MySQL query and adopts it to the Database class. If there are more than one rows returned by the result, it will return 	   		an indexed array of the rows. If there is only one row, it will return it, if there are no rows returned in mode 1, then it will 	   		return just the result resource.	   		Acceptable values for $mode:	   		  0 : just return the result resource by the query;	   		  1 : return the entire row collection, row, or resource if no rows available. 	   		$params:	   		  assigns parameters to be replaced in the SQL query.	 */	 function query($sql,$mode=0,$params=false){		 if($params!==false && is_array($params)){			 $this->params+=$params;		 }		 $sql = preg_replace('/\:([a-z]+([a-z0-9\_]+)?)/ie',"(isset(\$this->params['\\1'])) ? \$this->params['\\1'] : ''",$sql);		 $ans = $this->lastQuery =  @mysql_query($sql,$this->msa);		 //Stores the resource in this variable for use later		 $this->count = (!mysql_num_rows($ans)) ? mysql_affected_rows($ans) : mysql_num_rows($ans);		 //If its a select statement, return num_rows, if its an different statement, use affected		 		 if($mode==0){			 return $ans;		 }else{			 if($this->count>1){				 $rows = array();				 while($row = mysql_fetch_assoc($ans)){					 $rows[]=$row;				 }				 return $rows;			 }elseif($this->count!=0){				 $row = mysql_fetch_assoc($ans);				 $this->freeresult();				 return $row;			 }else{				 return $ans;			 }		 }	 }	 	 function fetchrow($ans=false){		 $ans = (!$ans) ? $this->lastQuery : $ans;		 $rows = $this->numrows($ans);		 if($rows > 1){			 $returnable = array();			 while($row = mysql_fetch_assoc($ans)){				 $returnable[]=$row;			 }		 }else{			 return mysql_fetch_assoc($ans) or false;		 }	 }	 	 function numrows($ans=false){		 $ans = (!$ans) ? $this->lastQuery : $ans;		 $rows = @mysql_num_rows($ans);		 $aff = @mysql_affected_rows($ans);		 if(!$aff){			 return $rows;		 }else{			 return $aff;		 }	 }	 	 function freeresult($ans=false){		 $ans = (!$ans) ? $this->lastQuery : $ans;		 //Hacker saver;		 		 if(@mysql_free_result($ans)){			 $this->lastQuery = '';			 return true;		 }else{			 return false;		 }		 	 }	 	 function close(){		 mysql_close();		 //Closes the db class		 $this->lastQuery = '';		 $this->msa = false;		 	 }	 	 function __destruct(){		 $this->close();	 }	  }?>

By the way: I know there's no mysql_real_escape_string calls and etc, i do that in a function that isn't defined here. you can add it if you like.

Link to comment
Share on other sites

I've got a few questions:1- Can you explain me all this params thing? What are those for again?2- Could you explain me these lines??

$ans = (!$ans) ? $this->lastQuery : $ans;$rows = @mysql_num_rows($ans);

In the second one, what's the @ for?? I forgot it.

Link to comment
Share on other sites

I've got a few questions:1- Can you explain me all this params thing? What are those for again?2- Could you explain me these lines??
$ans = (!$ans) ? $this->lastQuery : $ans;$rows = @mysql_num_rows($ans);

In the second one, what's the @ for?? I forgot it.

Parameters to the DB I believe?The "@" mutes error displaying (if any), so that no error information would be "echo"-ed at that point.Why not use PEAR's MDB2 btw? And Zend Framework with its Zend_Auth class?
Link to comment
Share on other sites

$sql = "SELECT * FROM users WHERE username = ':user' and pw=':userPw'";$db->query($sql,1,array('user'=>$_POST['userName'],'userPw'=>$_POST['pw']));

The db class would automatically parse the :userPw and :user and replace them with the values in the _POST array. Like i said, i have a different function to protect from SQL injections which i use for everything, so i saw no point in making it a part of the DB class. I took the idea from the PDO PHP object, which is also another thing you could use instead.Oh and the second thing, the $ans = (!$ans) ? $this->lastQuery : $ans; is to see if a the user submitted a mysql_query() resource variable, or if we should use the one stored from the last db query.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...