Jump to content

administrator login script trouble :/


rootKID
 Share

Recommended Posts

yoyo W3S :)

 

Ok, so to make this one short. My script is "supposed" to work, and it does on the login. However it is supposed to send the user "back" to the login page when no results has been found from the query i'm making... this is my login script, should make sence to you also i think, very simple login script so far :)

$username = $_POST['username'];$password = sha1($_POST['password']);$query = "SELECT u_id, username, password FROM users WHERE username='$username' AND password='$password'";$result = mysqli_query( $db_connection, $query );$num = mysqli_num_rows($result);if($num == 1){	// Fetch the result...	$row = mysqli_fetch_array($result);		$_SESSION['admin']['u_id'] = $row['u_id'];	$_SESSION['admin']['username'] = $row['username'];	$_SESSION['admin']['password'] = $row['password'];		header("location: index.php");}else{	// Send user back to login page with error...	header("location: login.php?err=1");}

However, it sends me to the index.php, even when there's an error and NO users with the damn name i'm typing... my question is now, what have i done wrong here?

 

Thanks in advance! :)

Mr r ;)

Link to comment
Share on other sites

  • Replies 51
  • Created
  • Last Reply

Top Posters In This Topic

Comment out the headers for now and check the values of the variables $query, $result and $num using var_dump() to see if they contain what you think they do.

 

Your script is vulnerable to SQL injection. If I use ' OR '1 as the username I can log in without a username or password. Use prepared statements instead of putting variables right in your query. I hope your index.php page is checking the login details before showing any content.

 

There probably are PHP errors on your page, because it doesn't look like you've created the array $_SESSION['admin'] before putting values into it. There should be a line somewhere like this:

$_SESSION['admin'] = array();

Link to comment
Share on other sites

For the security part, any suggestions? So far i have this at ALL my non-login admin pages:

 

PHP function at top:

user_check("u_id");

And this is the functions i use, still in the coding proccess tho :)

function start_my_session(){	if( session_id() == '' )	{		session_start();	}}function user_check( $name ){	# To make sure "session_start();" is started!	start_my_session();		if( !isset( $_SESSION['admin'][$name] ) )	{		header("Location: login.php");		exit();	}}

for the admin part, i forgot to add the stupid array, even tho when i check it with "$_SESSION" and call it out with a normal print_r, it's being created... is your way much better etc?

 

Will edit it tomorrow, it's about 23:33 in Denmark and i need to sleep :P

Making the changes tomorrow and sees what happens :)

 

Thanks! Cya tomorrow ;)

Link to comment
Share on other sites

For the security part, any suggestions?

You need to switch any query that uses variables in the query to be a prepared statement. Never put variables directly in a SQL query, always use prepared statements instead.http://php.net/manual/en/mysqli.quickstart.prepared-statements.php
Link to comment
Share on other sites

what you mean with that? I did read something about that part i mean in the SQLI, however i'm still learning how to use the SQLI since im used for the normal MySQL, and used simple variables inside the query then.

 

Example:

/* Prepared statement, stage 1: prepare */if (!($stmt = $mysqli->prepare("INSERT INTO test(id) VALUES (?)"))) {    echo "Prepare failed: (" . $mysqli->errno . ") " . $mysqli->error;}

Not sure what "$stmt" part means, and same with "VALUES(?)" part... what does the question mark? And what does the "prepare" function? Can you tell me in a understandable form? lol hehe :P

 

But besides that i would be more than happy to try it out tomorrow. One more question, i noticed this in the second stage:

/* Prepared statement, stage 2: bind and execute */$id = 1;if (!$stmt->bind_param("i", $id)) {    echo "Binding parameters failed: (" . $stmt->errno . ") " . $stmt->error;}if (!$stmt->execute()) {    echo "Execute failed: (" . $stmt->errno . ") " . $stmt->error;}

The "bind_param("i")"... what does the function and what does the i means? Is there a reference somewhere? Can't seem to find it... or maybe just me -.-'...

and the execution function is the one that runs the whole SQL i assume? :)

 

Thanks in advance! Talk to you all tomorrow ;)

Mr R ;)

Link to comment
Share on other sites

Everything is in the PHP manual.

Here's the reference page for the bind_param() method: http://php.net/manual/en/mysqli-stmt.bind-param.php

The execute() method: http://php.net/manual/en/mysqli-stmt.execute.php

 

First you prepare the statement: http://php.net/manual/en/mysqli.prepare.php

In the SQL you use "?" where variables would go. Example:

SELECT id, username FROM users WHERE id=? AND password=?

 

Then you bind variables to the placeholders using bind_param(). the first parameter is a string, each letter indicates the type of the variables that are used as the rest of the parameter as in this example:

$stmt = $mysqli->prepare("INSERT INTO CountryLanguage VALUES (?, ?, ?, ?)");// $code, $language and $official are stringe, $percent is a floating point number (double)$stmt->bind_param('sssd', $code, $language, $official, $percent);

Finally, you execute the query with execute()

 

There are two ways to get results. One is calling the get_result() method, the other is binding variables to the output using bind_result() and calling fetch()

 

Getting results using bind_result:

// $name and $code would have the values of the first two fields of the SELECT statement// Possible query: SELECT countryName, countryCode FROM countries$stmt->bind_result($name, $code);while($stmt->fetch()) {    echo $name . ', ' . $code . '<br>';}

The PHP manual pages I linked to are full of examples.

Link to comment
Share on other sites

quetion, what is my password is sha1? Is that still being detected as "s"? :)

 

EDIT

 

This is my code i have so far, not sure if it's correct tho, but did my best to understand it right out from you have told me and from what i was reading.

 

CODE:

# Database Settings & Setuprequire_once("../database/db.core.php");ob_start();session_start();# Functions (Login) Libraryrequire_once("functions/functions_login.php");// ======================================================$username = $_POST['username'];$password = sha1( $_POST['password'] );// Prepare & Binding$stmt = $mysqli->prepare("SELECT u_id, username, password FROM users WHERE username=? AND password=?");$stmt->bind_param('ss', $username, $password);if ($result = $mysqli->query($stmt)){	/*	while ($row = $result->fetch_row())	{		// Code goes here...	}	*/		$row = $result->num_rows;	if($row == 1)	{		$_SESSION['admin'] = array();		$_SESSION['admin']['u_id'] = $row['u_id'];		$_SESSION['admin']['username'] = $row['username'];		$_SESSION['admin']['password'] = $row['password'];				// free result set		$result->close();				// Send user to ACP page		header("location: index.php");	}	else	{		// Send user back to login page with error...		header("location: login.php?err=1");	}}// Closing "statement" AND "connection"$stmt -> close();

What i'm trying to do here is to check if only 1 row returns, and if true i wish to proceed with making the sessions as normal and let the user/admin pass to the admin page. However if no results/single row were affected/returned, then that user is being sent back to the original login page WITH an error. Is this code correct understood? I got a little bit confuced tho when you started to talk about stmt and all that, but what you think so far? Am i correct? And what about the SHA1 thing? Is that still detected as an "s"? :)

 

Thanks in advance! :)

Edited by rootKID
Link to comment
Share on other sites

You don't use $mysqli->query() anymore because this is a prepared statement. Use $stmt->execute()

 

The sha1() function returns a string. You can see that in the PHP manual: http://php.net/sha1

 

On that note, don't use SHA1 to encrypt passwords, it's easily broken by brute force. Use PHP's crypt() method for password encryption.

Here's PHP's warning right in the manual:

Note
:
Secure password hashing

It is not recommended to use this function to secure passwords, due to the fast nature of this hashing algorithm. See
here
for details.

 

 

Link to comment
Share on other sites

does the crypt method works like the sha1? just replacing the crypt with sha1?

 

i will test when home, writing back to you later. Thanks! :)

Link to comment
Share on other sites

The crypt() method uses a $salt parameter which is also used to determine which encryption algorithm the function will use. Read the documentation because it is important: http://php.net/crypt

 

While you technically are allowed to omit the $salt parameter, doing so will make the password much less safe.

 

If you have PHP 5.5 or above, the password_hash() function is available which is easier to use: http://php.net/manual/en/function.password-hash.php

Link to comment
Share on other sites

i know that it's much safer. But how to use? I'm not sure if i got this one correct, tell me if im wrong etc:

 

On the example 1, it has a "if" statement where crypt is used. Now my question is, inside the hash_equals function, what does the "$user_input" stand for? Is that the password the user has entered inside the input field? Or is that the password that has been hashed already from the "$hashed_password"?

 

And not sure but crypt is that not just another way of identifying passwords instead of encrypting them? Or am i wrong in the understandment?

 

Possible you could give me a smaller example that i could follow? I better understand it the way you say it i think for some reason, lol hehe x)

 

Thanks again! :)

Link to comment
Share on other sites

The first parameter of the crypt() function is the string you want to encrypt. The second parameter is the salt. The syntax of the salt determines which encryption algorithm is used.

$encryptedPassword = crypt($_POST['password'], '$2a$07$usesomesillystringforsalt$');

The salt syntax is explained in the manual. Here's an excerpt explaining the salt I used in the example:

CRYPT_BLOWFISH - Blowfish hashing with a salt as follows: "$2a$", "$2x$" or "$2y$", a two digit cost parameter, "$", and 22 characters from the alphabet "./0-9A-Za-z". Using characters outside of this range in the salt will cause crypt() to return a zero-length string. The two digit cost parameter is the base-2 logarithm of the iteration count for the underlying Blowfish-based hashing algorithmeter and must be in range 04-31, values outside this range will cause crypt() to fail. Versions of PHP before 5.3.7 only support "$2a$" as the salt prefix: PHP 5.3.7 introduced the new prefixes to fix a security weakness in the Blowfish implementation. Please refer to » this document for full details of the security fix, but to summarise, developers targeting only PHP 5.3.7 and later should use "$2y$" in preference to "$2a$".
Link to comment
Share on other sites

Now my question is, inside the hash_equals function, what does the "$user_input" stand for? Is that the password the user has entered inside the input field?

Yes, that's why it's called $user_input.

Or is that the password that has been hashed already from the "$hashed_password"?

No, $user_input is not the hashed password. $hashed_password is the hashed password.

And not sure but crypt is that not just another way of identifying passwords instead of encrypting them?

It is a function to hash a string with a salt.I would recommend doing some research about this stuff, the PHP manual is full of examples and comments from other people. There's another good way to hash passwords here:http://jeremykendall.net/2014/01/04/php-password-hashing-a-dead-simple-implementation/
Link to comment
Share on other sites

SHA512 alone, without a salt, isn't much better than SHA-1. SHA-1 with a salt is better than SHA512 without a salt. The link I posted in my previous post additionally factors in cost, which is another good measure. I try to target my hashing algorithm so that it requires around .25 seconds on our servers to calculate the hash. That's a high enough cost to cause problems for brute force attempts, but users don't notice it when logging in.

Link to comment
Share on other sites

Hi, sorry for late reply, bad food gave me food poison (crap :/)... but to keep to topic here, thanks for the reply!

 

I think i'm getting there slowly, this is my code at the moment, not sure if i understood it correct, but how does it look now?

 

CODE:

# Database Settings & Setuprequire_once("../database/db.core.php");ob_start();session_start();# Functions (Login) Libraryrequire_once("functions/functions_login.php");// ======================================================// Catch user details!$username = $_POST['username'];$password = crypt( $_POST['password'], '$2a$07$usesomesillystringforsalt$' );// Statement - Prepare & Bindings$stmt = $mysqli->prepare("SELECT u_id, username, password FROM users WHERE username=? AND password=?");$stmt->bind_param('ss', $username, $password);if ($result = $stmt->execute()){	/*	while ($row = $result->fetch_row())	{		// Code goes here...	}	*/		$count = $result->num_rows;	if($count == 1)	{		$row = $result;		$_SESSION['admin'] = array();		$_SESSION['admin']['user_id'] = $row['user_id'];		$_SESSION['admin']['user_username'] = $row['user_username'];		$_SESSION['admin']['user_password'] = $row['user_password'];				// free result set		$result->close();				// Send user to ACP page		header("location: index.php");	}	else	{		// Send user back to login page with error		header("location: login.php?err=1");	}}// Closing "statement" AND "DB connection"$stmt -> close();

Is this correct understood? I will change the string in the crypt if this is correct understood...

 

Another question. My administrator can't signup as a user, this means i have to pre-insert the user for the administrator in the database.

My question is now this, what do i need to do to get the correct code to the database user? I'm using SHA1 atm from before. Ideas? :)

 

And thanks for the help and understandment so far, still learning how to work out the MySQLI work x)

Thanks, checking back later! :)

 

Mr R ;)

Link to comment
Share on other sites

Check this page: http://ca2.php.net/manual/en/mysqli-stmt.fetch.php

Example #1 Object oriented style.

 

First you execute the statement, but the execute() function does not return results. You need to use fetch() for that.

Right from the example:

/* execute statement */$stmt->execute();/* bind result variables */$stmt->bind_result($name, $code);/* fetch values */while ($stmt->fetch()) {    printf ("%s (%s)n", $name, $code);}
Link to comment
Share on other sites

ok now im confuced, this was my code before:

//$stmt->execute(); // This part was in the "if" statement below before...//$mysqli->prepare("SELECT u_id, username, password FROM users WHERE username=? AND password=?");//$stmt -> bind_param('ss', $username, $password);if ($result = $stmt->execute()){	$count = $result->num_rows;	if($count == 1)	{		//$row = $result;		$row = $stmt->fetch();		$_SESSION['admin'] = array();		$_SESSION['admin']['user_id'] = $row['user_id'];		$_SESSION['admin']['user_username'] = $row['user_username'];		$_SESSION['admin']['user_password'] = $row['user_password'];				// free result set		$result->close();				// Send user to ACP page		header("location: index.php");	}	else	{		// Send user back to login page with error		header("location: login.php?err=1");	}}// Closing "statement" AND "DB connection"$stmt -> close();

This is my code now, the way i understand what you tell me anyways x)

// Catch user details!$username = $_POST['username'];$password = crypt( $_POST['password'], '$2a$07$usesomesillystringforsalt$' );// Statement - Prepare & Bindings$query = "SELECT user_id, user_username, user_first_name, user_last_name, user_password FROM users WHERE username=? AND password=?";if ($stmt = $mysqli->prepare($query)){	// execute statement	$stmt->execute();		// bind result variables	$stmt->bind_result($username, $password);	//$stmt -> bind_param('ss', $username, $password);		// close statement	$stmt->close();}// close connection$mysqli->close();

question, what is the difference between "bind_param" and "bind_result"? Just noticed it changed?

 

Maybe just me but you just made me change the whole login script, or did i misunderstand you out from what you said again?

If possible can you show me an example so i can understand you better? I understood the execute part, or so i think... but unsure about in which order i should code the login script our for what information i got so far :/

 

Ideas? xP

Link to comment
Share on other sites

Bind_param is used for the input parameters, bind_result() is for the results of that query.

I've fixed up your script:

// Catch user details!$username = $_POST['username'];$password = crypt( $_POST['password'], '$2a$07$saltgoeshere$' ); // Make the salt unique for each password// Statement - Prepare & Bindings$query = "SELECT user_id, user_username, user_first_name, user_last_name, user_password FROM users WHERE username=? AND password=?";if ($stmt = $mysqli->prepare($query)){    // Input parameters that are put into the query before you execute it    $stmt -> bind_param('ss', $username, $password);    // execute statement    $stmt->execute();        // Set output variables, these are the data from the database fields, user_id, user_username, user_first_name, user_last_name and user_password    $stmt->bind_result($result_id, $result_username, $result_firstname, $result_lastname, $result_password);    // Fetch data from the query    $stmt->fetch(); // That's it, nothing more.    // Use the variables however you like:    echo "The data is: {$result_id}, {$result_username}, {$result_firstname}, {$result_lastname}";    // close statement    $stmt->close();}// close connection$mysqli->close();
Link to comment
Share on other sites

Ok, i think i almost understand what this is about.

I have one more question, when you say in the bind result part, is this where that variable are being created all the time? I mean if i wanted to edit my query later on for other purposes, would i need to add those the the bind_result function but in an order of state? Meaning if the "id,username,password" came like that, it would HAVE to be "ID, NAME, PASS" for ordering the variables i can use? :)

 

Thanks btw! Makes alot more sence now! xD

 

And one last, is this the same method i use if i wish to upload a file to my server? In this case just take the file-name when i get that far and use that filename to insert to the DataBase? :)

 

Thanks again! :D

Edited by rootKID
Link to comment
Share on other sites

Ok i'm not sure but i was checking out what was avaiable of the "crypt" part and found the SHA-500 and tried to switch to that part, just to see if anything happened and how it looked. I made a simple "echo" with the crypt and got this:

$6$rounds=5000$SimpleSourceMade$Df4j02HQ/jkXYqdaKIA6D7mAUhCfaZo9i0QnU4.vw5ELmARdYGRm6yVDrl8fYTPcZ4YcSdZyWRIVf4UqTooJl/

I then tried to copy/paste this code to my already pre-made user in the database, i could not login with this one.

Then i tried to only take this part of the code:

Df4j02HQ/jkXYqdaKIA6D7mAUhCfaZo9i0QnU4.vw5ELmARdYGRm6yVDrl8fYTPcZ4YcSdZyWRIVf4UqTooJl/

Still could not login. My question now is if i have something spelled out wrong in my code etc?

 

This is my code right now:

// Catch user details!$username = $_POST['username'];$password = crypt( $_POST['password'], '$6$rounds=5000$SimpleSourceMadeReal$' ); // Make the salt unique for each password// Statement - Prepare & Bindings$query = "SELECT user_id, user_username, user_first_name, user_last_name, user_password FROM users WHERE username=? AND password=?";if ($stmt = $mysqli->prepare($query))//if ($stmt = $mysqli->prepare($query) && $mysqli->num_rows == 1){	// Input parameters that are put into the query before you execute it	$stmt->bind_param('ss', $username, $password);		// execute statement	$stmt->execute();		// Set output variables, these are the data from the database fields:	// -> user_id, user_username, user_first_name, user_last_name and user_password	$stmt->bind_result($id, $username, $firstname, $lastname, $password);		// Fetch data from the query	$stmt->fetch(); // That's it, nothing more.		$_SESSION['admin'] = array();	$_SESSION['admin']['user_id'] = $id;	$_SESSION['admin']['username'] = $username;	$_SESSION['admin']['firstname'] = $firstname;	$_SESSION['admin']['lastname'] = $lastname;	$_SESSION['admin']['password'] = $password;		// close statement	$stmt->close();		// close connection	$mysqli->close();		header("location: index.php");}else{	// Send user back to login page with error	header("location: login.php?err=1");}//////////////////////////////////////////////////////// ONLY USE IF NOT USING//(&& $mysqli->num_rows == 1)// IN THE IF/ELSE STATEMENT ABOVE!// - REMEMBER TO COMMENT OUT THE ELSE STATEMENT// - IF I REMOVE THAT PART AND MOVE BACK TO THE OLD// - LOGIN METHOD!//////////////////////////////////////////////////////// close connection//$mysqli->close(); 

As you see i have "tried" to edit the script to return to the login page with an error like before, and if it got back with any users it should make the sessions and then return to the administrator page.

 

Not sure if i made anything wrong or something, but the way i see it it should be working... any ideas Oo?

Thanks! :)

Link to comment
Share on other sites

The result of the crypt function is what you should store in the database. There is no need to remove anything from the value given by crypt, keep all of it.

 

If you pass the very same parameters to crypt, it should yield the exact same result. You must use the same salt when comparing a password as you did when you created the one that's in the database.

 

If the comparison is failing, print out both the database value and the user input to see why.

 

Remove the location headers until you're done testing, otherwise you won't be able to see any output given by the script.

Link to comment
Share on other sites

You shouldn't use the same salt for every user, add another column in your database for the salt for each user so that you can store a different salt for each user. Using the same salt for everyone partially defeats the purpose of using salts. When you are logging in a user you should look up their record based on their username, get the salt from the record, use the salt to hash the password they entered, and compare it with the hash in their record.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share


×
×
  • Create New...