I've currently been pulling my hair out on this problem for about a full day now. I am developing my first application. I'm actually pretty far into it (having established the login page several weeks ago, worked perfectly). Last night, I ran my app and it worked perfectly. I backed it up to my flash drive, tested it once more, and closed my laptop down for the night.
This morning I opened up the app to begin the documentation process on it -- this is a project for my capstone so I have to document/video the outcome each week -- and suddenly my tester account couldn't be authenticated. I tried registering a new one via the app, and it doesn't post the data to the database. Naturally, I assumed the database/server was having issues and contacted my host; in a two-hour long process, they verified that the database was working and it was simply in my code which hadn't been touched since the night before when it ran perfectly.
I've read that this sometimes happens with authentication through FireBase (i believe this is what it is called) and OAuth. I'm using neither and just doing the basic database reading/writing. I've not found any useful information on what to do through Google, probably due to the fact that the problem is just too complicated for a quick search... or I just don't know how to word it correctly.
<?php
$connection = mysqli_connect("localhost", "SENSORED", "SENSORED", "SENSORED");
$username = $_POST["username"];
$password = $_POST["password"];
$statement = mysqli_prepare($connection, "SELECT * FROM user WHERE username = ? AND password = ?");
mysqli_stmt_bind_param($statement, "ss", $username, $password);
mysqli_stmt_execute($statement);
mysqli_stmt_store_result($statement);
mysqli_stmt_bind_result($statement, $user_id, $name, $username, $email, $company_id, $phone, $password, $leaveTime, $sickTime, $rateOfPay);
$response = array();
$response["success"] = false;
while(mysqli_stmt_fetch($statement)){
$response["success"] = true;
$response["user_id"] = $user_id;
$response["name"] = $name;
$response["username"] = $username;
$response["email"] = $email;
$response["company_id"] = $company_id;
$response["phone"] = $phone;
$response["password"] = $password;
$response["leaveTime"] = $leaveTime;
$response["sickTime"] = $sickTime;
$response["rateOfPay"] = $rateOfPay;
}
echo json_encode($response);
?>
I did set up a connection test, which verified that the PHP is indeed connecting to the database and querying it. It can tell how many tables I have and, for example, how many usernames I have in the user table. However, when I enter:
"SELECT * FROM user WHERE username = 'test'"
being that there is a username that is just 'test', it cannot find a match. However, I can see from looking at the database that test is indeed a username and does exist in that table.
I currently am getting no results via PHP. However, if I query via PHPmyadmin, it displays the row that contains 'test' as a username. Obviously, my coding is correct, but I'm not sure what could be interfering with selecting it from the PHP since it is establishing a connection and reading from the database. Not to mention the fact that it isn't wanting to write the registration information to the database when there are no holds on that.
I've gone as far in looking for a solution as creating a whole new database with different credentials and such and receive the same problem. Google has been no help in the search for an answer, and I haven't managed to find a similar question/problem on here.
Any idea how a working database connection and reading/writing abilities can completely disappear without being edited overnight?
this is how I would do it. As I said i haven't used MySqli in a number of years so I had to do some googling, and I am not 100% sure everything is correct as I can't really test it.
But here goes.
//turn on output buffering - useful for debugging
ob_start();
//I would use the object interface
$mysqli = new mysqli("localhost", "SENSORED", "SENSORED", "SENSORED");
//your not checking the connection success
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
$username = $_POST["username"];
//are you storing passwords in plain text?
$password = password_hash($_POST["password"]);
/*
I would not pick the password from the DB, the DB is case insensitive,
unless you use collation UTF8_bin or such.
Also it's harder to debug, as you don't know if the user exists or the
password matching is wrong.
*/
$stmt = $mysqli->prepare($connection, "SELECT * FROM user WHERE username = ?");
//bind the inputs
$stmt->bind_param("s", $username);
//execute the query
$res = $stmt->execute();
//fetch results as an associative array
$row = $res->fetch_assoc();
//set default
$response["success"] = false;
/*
We expect exactly 1 row be returned. More then 1 should
be impossible if your username is unique (which is should be)
but just in case any issues happen we should check that it's exactly
1 return row, no more no less.
*/
if($res->num_rows() != 1) {
//always be cordial in your error messages to users
$response['message'] = "We're sorry we could not find user {$username}";
return $response;
}
//check the password hash using an approved crypto-logically safe method
if(!hash_equals($password, $$row['password']) {
$response['message'] = "Unfortunately the password you entered is incorrect";
return $response;
}
//remove the password / we no longer need it.
//security is paramount, no need to risk exposing it, even when its hashed
//eg. I just spent the weekend fixing 5 wordpress sites that were hacked
unset($row['password']);
$response['message'] = "Success!";
//put the contents of the buffer into debug. only do this on localhost
//you could switch this using the HOST or server IP
$response['debug'] = ob_get_clean();
//combine the row with the response
//caution if key matched in rows exists in response value in row
//will be replaced with that of response ( it shouldn't be a problem in this case )
$response += $row;
//set the correct content type, this allows browsers to
//know this is json, and prevents having to do JSON.parse() in some cases
header('Content-type:application/json');
//encode the responce
echo json_encode($response);
I put some notes in there such as this
//always be cordial in your error messages to users
There was once a young web developer who had a client that complained his error messages were to "cold". But this young web developer took his clients criticism to heart and remembered to always be extra nice to end users. ( that was around 2009). That said, it hasn't improved my spelling.
One thing I ran across while refactoring this, is I noticed you are not encrypting the password. I don't know if this is by mistake or by design.
ob_start and ob_get_clean allow you to echo or print_r stuff between them without breaking the JSON response. Normally if you create output then it will be outside of your json data
here is some output
{"success" : false}
Which will cause errors on the client side. With output buffering the above will look like this
{"debug" : "here is some output", "success" : false}
I suppose it's optional, but it's the right way to do JSON or even any content type other than HTML, including (or especially) file downloads.
Hope it helps. Even if you cant use the code in this form, it may help seeing it written a different way. Sometimes you can be to close to the problem and overlook some simple mistake. Looking at it in another format and having to process the logic of it, may shed light on something you missed.
Related
This is the code for my log in forum. The problem with it is that it only accepts as correct credentials the first username and password (basically only the first row) any ideas as to how i could change it ?!
<?php
session_start();
include_once("connect.php");
$token = "";
if($con->connect_error){
die("Connection failed: ".$con->connect_error);
}
$sql = "SELECT * FROM authme";
$result = mysqli_query($con, $sql) or die(mysqli_error($con));
while(mysqli_num_rows($result)>0){
while($row = $result->fetch_assoc()){
if(isset($_POST['realname']))
$username = $_POST['realname'];
if($result->num_rows>1){
if(mysqli_num_rows($result)>1){
$_SESSION['uid'] = $row['id'];
$_SESSION['realname'] = $row['realname'];
}
$password = '$SHA$'.substr($row['password'],5,16).'$'.hash('sha256', hash('sha256',$_POST['password']).substr($row['password'],5,16));
if($password == $row['password'] ){
header("Location: index.php");
exit();
}
else {
echo "INVALID INFORMATION, PLEASE RETURN!";
// header("location: index.php");
session_destroy();
exit();
}
}
}
}
?>
?
I decided to try to make a log in forum that uses a database which encrypts the passwords it receives through a register form. This code only takes as correct the first username and password i give in and its not enough, as you could imagine.
Welcome to programming with PHP. I'm going to try to share a few principles that may help you solve your problem.
1.) One of the best features in PHP is the print_r() function. Using this function you can output almost anything to text in the browser. So in this case you may want to insert a print_r($result) immediately following this line "$result = mysqli_query($con, $sql) or die(mysqli_error($con));". This will output the results of the query that PHP is receiving. This can be used to help you troubleshoot and determine why your code isn't working. Once you're done troubleshooting delete that line.
2.) You seem to have multiple checks for the number of rows inside the while loop. I'm not sure why you have thoose there, but you may want to check if those are causing your trouble by using echo or print to display to values in the browser for troubleshooting. Once you're done troubleshooting delete that line.
3.) Another overall concept for the data you are querying. It is inefficient to send a query that gets the entire table and returns it to the program, that then loops through every row looking for the data. Instead you should write an SQL query to return only the row of data the you want. Make sure you do use prepared statements.
4.) Your coding standards could use some improvement, if you clearly tabbed your statements it would be easier to read. Consider reading PSR-2. For example this code seems to be missing {}'s.
if(isset($_POST['realname']))
$username = $_POST['realname'];
I am attempting to create new tables every time I post to this method, but for some reason I can not figure out why it dies.
<?php
$host = "127.0.0.1";
$username = 'cotten3128';
$pwd = 'pwd';
$database = "student_cotten3128";
$pin = $_REQUEST['pinSent'];
$words = $_REQUEST['resultSent'];
$tableName = $pin;
$db = new mysqli($host, $username, $pwd, $database);
if ($sql = $db->prepare("CREATE TABLE $pin (id INT(11) AUTO_INCREMENT);")) {
$sql->execute();
$sql->close();
}else{
echo $mysql->error;
die('Could not create table');
}
for($i=0;$i<count($words);$i++){
if($sql = $db->prepare("INSERT INTO ".$pin.$words[$i].";")) {
$sql->execute();
$sql->close();
}else{
echo $mysql->error;
die("Could not add data to table");
}
}
mysqli_close();
?>
Any help or insight would be greatly appreciated.
The intention of my post is to help you finding the issue by yourself. As you did not added much information I assume my post is helpful for you.
Based on the code you have shared I guess you mean one of your called die() functions is executed.
Wrong function call
As Jay Blancherd mentioned mysql_close is the wrong function. You rather have to use mysqli_close as you created a mysqli instance.
Beside of that mysql_* is deprecated and should not be used anymore.
Debugging Steps
Not only for this case but in general you should ask yourself:
Is there an error message available? (Frontend output, error log file, ...)
YES:
What's the message about?
Is it an error you can search for? E.g. via a search engine or the corresponding documentation?
Look up in the bug tracker (if available), by the software developer of the software you are using, and if it has not been reported yet report the issue.
NO: (if none error message available OR you cannot search for it as it is a custom error message)
Search in the files of the software you are using for the error message and start a core-debugging.
STILL NO SOLUTION?:
Ask on stackoverflow.com e.g. and tell your issue and the steps you have performed to find and fix the bug. Post only as much code as necessary plus use a proper format.
Debugging in your case:
In order to narrow down the scope. Which of the die() is executed? Depending on that echo the query to execute just before it actually is executed. Then copy the SQL query to an SQL editor and look at it syntax. After that you probably know the problem already.
I´m not very familiar with security, therefore I rely on what I find on the internet. I found a site of someone who explains a bit what he does and how his method works. People may copy-paste it to ease things up.
Though I do understand quite a lot, I couldn't come up with it myself (I'm pretty new to PHP/XHTML, etc.)
The website: How to store safely with PHP and MySQL
He uses PDO in his tutorial. And I am able to store the information in the database. But when I try to use the script in which he provides the code for actually logging in, though it seems it contains errors.
I've worked everything out and everything works fine, but the comparison of the hashed password with the inserted password (with the hash, etc.) does not work properly.
What is going on here?
Thanks in advance!
EDIT
People have been asking for the code so, here it is:
session_start();
require('config.php');
// Setting up a connection
$MyConnection = new PDO('mysql:host=*;dbname=*', $dbuser, $pass);
// Retrieving information from form.
$username = $_POST['username'];
$password = $_POST['password'];
$sth = $MyConnection->prepare("SELECT * FROM AMP_Users WHERE Username = :username LIMIT 1");
$sth->bindParam(':username', $username);
$sth->execute();
$user = $sth->fetch(PDO::FETCH_OBJ);
// Hashing the password with its hash as the salt returns the same hash
if (crypt($password, $user->hash) == $user->hash) {
echo 'You are now logged in. If we actually used sessions this time.';
}
I will add a $_SESSION['name'] = $username, once the code starts to work. Until now I simply echo out if it worked out or not. And it doesn't show anything, so it doesn't work.
SECOND EDIT
Just as a quick update, the script provided by me, is the WHOLE script. Nothing is let out. (Except names of databases, etc.)
Therefore I wonder if the problem may be that I don't use the hashing script of the saving the passwords into the database. Though I have put it in, it still doesn't respond. Am I still doing something wrong?
if($_POST):
$name = $_POST['username'];
$pass = crypt($_POST['password'], '$2a$07$Hd893nD39Jdjd48Jdh3nD$');
$conn = new PDO('mysql:host=*; dbname=*', 'root', '');
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$stmt = $conn->prepare('SELECT * FROM user WHERE name = ? AND password = ?');
$stmt->execute(array($name, $pass));
if($stmt->rowCount() === 0){
echo 'Your Username / Password is incorrect. Please try again';
}else{
echo 'login success';
}
endif;
Maybe you have to check the length of the field that you store the password on the database... If the length is small then the hashed password will not stored as whole.. you will store a part of it!
I modified it to run in mysqli and it works fine:
$getAuth=$dbConAU->prepare("SELECT Password FROM Users WHERE UserName=? LIMIT 1");
$getAuth->bind_param("s",$UserName);
$getAuth->execute();
$getAuth->bind_result($hash);
$getAuth->fetch();
$getAuth->close();
if (crypt($Password, $hash) == $hash) {
return "OK";
}
else { return "Not OK"; }
I know this thread is a few months old but someone might find this
SunnyTuts php pdo login and registration tutorial
tutorial helpful. I found both this thread and the tutorial while looking for a secure way to allow users to login. Being new to php and web design I found it a small bit hard to follow but I'm sure it will seem like a piece of cake to some of you....
As part of a PHP web application, I'm querying a MySQL database using mysqli and prepared statements.
I've used exactly the same code on a few queries and it works, but on one particular query, it always returns an empty record set. I've run exactly the same query from the MySQL command line, and it correctly returns the result. I've checked the parameters being passed in, and they're fine.
I've spent the best part of a day trying to figure out why I'm always getting an empty record set with no errors or warnings. I've got PHP's errors set to display on the page, and I've got them set to E_ALL|E_STRICT. I still don't get any warnings or errors.
I've tried all the obvious things, like making sure I can actually connect to the database, checking the parameters that are being passed in, and making sure the row I'm trying to return actually exists in the database. I've had var_dump()s and die()s all over the page to check what's coming back, and it's always a legitimate, but empty, recordset.
function salt() {
return("I've removed my salt from this sample code");
}
function openDatabase() {
$conn = new mysqli("127.0.0.1", "username", "password", "database")
or die("Error: Could not connect to database.");
return($conn);
}
function checkUserCredentials($username, $password) {
$goodPassword = md5(salt().$username.$password);
$conn = openDatabase();
$query = $conn->stmt_init();
$query->prepare("SELECT id FROM users WHERE email = ? AND passwordHash = ?")
or die('Problem with query');
$query->bind_param("ss", $username, $goodPassword)
or die('Error binding parameters');
$query->execute() or die("Could not execute");
$query->bind_result($col1) or die ("Could not bind result");
if ($col1 !== 0) {
die("Authentication Complete");
} else {
die("Authentication Failure! Number of Rows: ".$query->num_rows." Username: " . $username . " Password Hash: " . $goodPassword);
}
}
Any feedback is appreciated. I'm sure I'm missing something simple, but if I didn't shave my head I'd be tearing my hair out right now.
Thanks
I'm not familiar with the mysqli library (I usually use PDO which provides a very similar cross platform API) so I can't immediately see any problem. However, you might try watching the mysqld log. See here for info:
http://dev.mysql.com/doc/refman/5.1/en/query-log.html
By tailing the log, you should be able to see the exact query that was submitted.
One final note, I notice you're using a fixed salt value. Wouldn't it be better to generate this value randomly each time you need it and then store it in the users table? Generally, a salt is not intended to be secret, it's just there to prevent people precomputing tables of passwords using the hash algorithm that you use.
In case anyone else runs into similar issues, it really helps if you run fetch() on your mysqli_stmt object.
In my code above, the solution looks like this:
$query->bind_result($col1) or die ("Could not bind result");
$query->fetch(); // <--- How could I forget to do this?
if ($col1 !== 0) {
return true;
} else {
return false;
}
Added on behalf of OP
Can anyone see anything wrong with this login script:
public function login($username, $pass, $remember) {
// check username and password with db
// else throw exception
$connect = new connect();
$conn = $connect->login_connect();
// check username and password
$result = $conn->query("select * from login where
username='".$username."' and
password=sha1('".$pass."')");
if (!$result) {
throw new depException('Incorrect username and password combination. Please try again.');
} else {
echo $username, $pass;
}
To explain:
At the moment the script is allowing anything through. In other words the query is returning true for any username and password that are passed to it.
I've put the echo statement just as a check - obviously the script would continue in normal circumstances!
I know that the connect class and login_connect method are working because I use them in a register script that is working fine. depException is just an extension of the Exception class.
The function login() is part of the same class that contains register() that is working fine.
I know that the two variables ($username and $pass) are getting to the function because the echo statement is outputting them accurately. (The $remember variable is not needed for this part of the script. It is used later for a remember me process).
I'm stumped. Please help!
UPDATE
Thanks for those responses. I was getting confused with what the query was returning. The complete script does check for how many rows are returned and this is where the checking should have been done. Everything is now working EXCEPT for my remember me function. Perhaps someone could help with that?!?! Here is the full script:
public function login($username, $pass, $remember) {
// check username and password with db
// else throw exception
$connect = new connect();
$conn = $connect->login_connect();
// check username and password
$result = $conn->query("select * from login where
username='".$username."' and
password=sha1('".$pass."')");
if (!$result) {
throw new depException('Incorrect username and password combination. Please try again.');
}
if ($result->num_rows>0) {
$row = $result->fetch_assoc();
//assign id to session
$_SESSION['user_id'] = $row[user_id];
// assign username as a session variable
$_SESSION['username'] = $username;
// start rememberMe
$cookie_name = 'db_auth';
$cookie_time = (3600 * 24 * 30);*/ // 30 days
// check to see if user checked box
if ($remember) {
setcookie ($cookie_name, 'username='.$username, time()+$cookie_time);
}
// If all goes well redirect user to their homepage.
header('Location: http://localhost/v6/home/index.php');
} else {
throw new depException('Could not log you in.);
}
}
Thanks very much for your help.
UPDATE 2!
Thanks to your help I've got the main part of this script working. However, the remember me bit at the end still doesn't want to work.
Could someone give me a hand to sort it out?
$username, $pass and $remember are all short variable names that I assigned before passing them to the function to save writing $_POST['username'] etc. everytime. $remember refers to a checkbox.
What does $conn->query() return, a MySQL resource object like mysql_query() does? If so then it'll always compare "true". mysql_query() only returns FALSE if the query completely fails, like it has a syntax error or a table doesn't exist.
To check if you got any results you need to try to fetch a row from the result set and see if you get anything, via whatever your equivalent of mysql_fetch_row() is.
Important: Your script is vulnerable to SQL injection attacks, or even just odd usernames like o'neil with an apostrophe. You should escape all variables in a query with mysql_real_escape_string() (or equivalent) to make sure your query doesn't get messed up by special characters. Or, even better, use prepared statements which look like
select * from login where username=? and password=sha1(?)
Re: UPDATE
Variables from a form are available via either $_GET or $_POST, depending on which method was used to submit the form. Try if (isset($_POST['remember'])) to see if that check box was checked.
Important: I see that you tried to use a bare $remember to see if the check box was checked. That suggests to me that you are trying to take advantage of the register_globals feature in PHP which makes your GET and POST variables accessible via regular variable names. If that is the case you should heed the warning in the PHP manual!
WARNING
[register_globals] has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 6.0.0. Relying on this feature is highly discouraged.
Use $_GET and $_POST instead. I could tell you how to make if ($remember) work, actually, but given the inherent evil-ness of register_globals I'm not gonna! ;-)
Your query is open for sql-injections...
SELECT * FROM users WHERE
username = '' OR 'a' = 'a'
AND password =
sha1('guessAnyPassword')
I'd also check your result, and base the action on how many records were returned.
if (mysql_num_rows($result) > 0)
In php most queries only return False if there was an error executing them. Your query is returning a value, probably an empty array of values. This is not a false value as far as your if statement is concerned.
Check how many rows are returned. The function to do this will depend on your abstraction layer (connect class etc...)