0

I am making a php registration script and registration works fine and everything is imputed into the database successfully and the activation email works just fine, but whenever I use a mysqli query to select or update info, it doesn't work.

For instance, when I log in with an account I know is in the database, it tells me the username doesn't exist, and when clicking the activation link in the email, the query fails to update the database in the same way.

I'm sure this is a super simple error I'm overlooking in my newbishness, but I couldn't find a suitable answer after a few hours of looking. I'm not really sure what the issue is.

Activate.php

require "/functions.php";

if (isset($_GET['user'])) {
  $user = $_GET['user'];
}
if (isset($_GET['key']) && (strlen($_GET['key']) == 32)){
  $key = $_GET['key'];
}

if (isset($user) && isset($key)) {
$sql = <<<SQL
  UPDATE users
  SET validation = NULL
  WHERE username='$user'
  AND validation='$key',
  user_group = 'member'
SQL;

$count = $db->affected_rows;

if ($count == 1)
{
    echo '<div>Your account is now active. You may now <a href="login.php">Log in</a></div>';

} else {
 echo '<div>Oops! Your account could not be activated. Please recheck the    link or contact the system administrator.</div>';

}
ob_end_flush();

} else {
  echo '<div>Error Occured .</div>';
}

?>

login.php

// Globals & error variable
require "/functions.php";

    session_start();
    $error = "";

if (isset( $_POST['Submit'])) {
    if (empty($_POST['username']) || empty($_POST['password'])) {
        $error = "Please fill in all fields!";
}
else {

    $username=$_POST['username']; 
    $password=$_POST['password']; 

    // Injection-protection!
    $username = stripslashes($username);
    $password = stripslashes($password);
    $username = mysqli_real_escape_string($db, $username);
    $password = mysqli_real_escape_string($db, $password);

$sql = <<<SQL
    SELECT *
    FROM `users`
    WHERE `username`='$username'
SQL;

    $result = $db->query($sql);
    $count->num_rows;

    if($count==1){
        while ($row = mysqli_fetch_array($result)) {
            $hash = $row['password'];
            $ug = $row['user_group'];
        }

        salt();

        $options=['salt'=>$bcrypt_salt, 'cost'=>12];
        $password=$argv[1];

        if (crypt($password,$hash) == $hash) {
            $_SESSION['login_user']= $username;
            $_SESSION['user_group']= $ug;
            header("location:index.php");
        }
        else {
            $error = "Username or password is invalid!";
        }
    }
    else {
        $error = "That username doesn't exist!";
    }
    ob_end_flush();
    }
}

functions.php

 // db connect
 $db = new mysqli('HOST', 'USER', 'PASS', 'DB');

if($db->connect_errno > 0){
   die('Unable to connect to database [' . $db->connect_error . ']');
}

On a side note, feel free to bring up any glaringly obvious bad-practice things you guys see. I'm new, but I don't want to develop bad habits!

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Miranda Roberts
  • 341
  • 1
  • 13
  • 2
    `$count->num_rows;` ?? Try `$count = $result->num_rows;` –  Jun 28 '15 at 02:23
  • in `Activate.php` you create `$sql`, but never execute it, just doing `$count = $db->affected_rows;`. You need a `$db->query($sql);`. note, in your `$sql` you directly use `$user` and `$key` without sanitizing them first. not a good idea. – Sean Jun 28 '15 at 02:27
  • @HoboSapiens Yep, I knew it was something simple I overlooked. Thank you so much, this fixed the login, but made no change to the activation. – Miranda Roberts Jun 28 '15 at 02:30
  • 1
    check the UPDATE query with maybe wrong comma `,` in WHERE clause – Deadooshka Jun 28 '15 at 02:35
  • @Sean Thank you for your comment. I added sanitation (thank you for the reminder) and added the $db->query($sql); to no avail. – Miranda Roberts Jun 28 '15 at 02:39
  • @Deadooshka Thank you for the comment! But removing that comma made no difference. ): – Miranda Roberts Jun 28 '15 at 02:41
  • 1
    missing an AND --- `AND validation='$key' AND user_group = 'member'` - checking for errors would have signaled that syntax error http://php.net/manual/en/mysqli.error.php – Funk Forty Niner Jun 28 '15 at 02:53
  • is this syntax correct?: `...$sql = << – CodeGodie Jun 28 '15 at 03:00
  • @CodeGodie that's valid syntax. It's called a heredoc https://php.net/manual/en/language.types.string.php#language.types.string.syntax.heredoc – Funk Forty Niner Jun 28 '15 at 03:01
  • 1
    interesting, never knew about it. Reminds me of Twig – CodeGodie Jun 28 '15 at 03:04
  • @CodeGodie There's also "nowdoc" on that same page also https://php.net/manual/en/language.types.string.php#language.types.string.syntax.nowdoc a bit different than heredoc; but interesting usage as well. – Funk Forty Niner Jun 28 '15 at 03:06
  • @MirandaRoberts I also need an example of what the username and keys are. Not actuals, but something close, if there are any special characters etc. I'll talk to you tomorrow. – Funk Forty Niner Jun 28 '15 at 03:45

1 Answers1

1

Besides what's already been stated in comments that you weren't executing that query (something I did spot though, before reading through), and now you are, there's this block of code, besides the extra comma in comments which you said was removed:

WHERE username='$user'
AND validation='$key',
user_group = 'member'

It's missing an AND --- AND validation='$key' AND user_group = 'member'

Checking for errors would have signaled that syntax error http://php.net/manual/en/mysqli.error.php

You should also add exit; after header, otherwise your code may want to continue executing/running.

Sidenote:

  • Keeping in mind what was already stated in comments about $count->num_rows; and using $count = $result->num_rows; instead and not executing the query $db->query($sql); for the activation file.

Add error reporting to the top of your file(s) which will help find errors.

<?php 
error_reporting(E_ALL);
ini_set('display_errors', 1);

// rest of your code

Sidenote: Error reporting should only be done in staging, and never production.


Footnotes:

Your present code for the activation file is open to SQL injection. Use prepared statements, or PDO with prepared statements, they're much safer.


Edit:

  • Please read the following over very carefully and there are notes included in my test code with commented instructions/information.

Using the code that follows below, proved to be successful for me.

Make sure that:

  • The column for the validation key is VARCHAR
  • Its length is 50, as a test but 32 may not be enough.
  • Use a minimum of 40 by altering the column's length first before new tests.
  • That the "validation" column accepts a NULL value.
  • You've chosen the correct database and table.
  • The validation key does not contain any trailing space(s).

MD5 produces a string length of 32, so you may have to increase it for your column, to say 40 or 50. My test used 50.

Your WHERE clause will depend on it.

If the string sent via Email contains a space, make sure there are no trailing spaces.

  • Use trim()
  • Make sure the entered key in the table doesn't initially contain a trailing space.
  • You will have to start over.

I.e.:

$key = $_GET['key'];
$key = trim($key);
  • Affected rows will not work if your table still contains "test" and the "b5bad6f02c247458e3d888f94b568819" test key that you are using.
  • In order for "affected_rows()" to truly work, you will need to start over with a clear table for the "user" and "key".
  • So, clear those out before running a new test.

I would also get rid of this conditional statement if (isset($user) && isset($key)) { along with its accompanying closing brace }.

  • It may be doing more harm than good.

You are already using a similar method in this block:

if (isset($_GET['user'])) {
  $user = $_GET['user'];
}
if (isset($_GET['key']) && (strlen($_GET['key']) == 32)){
  $key = $_GET['key'];
}

However, I would modify that to: (and to test)

if (isset($_GET['user'])) {
  $user = $_GET['user'];
}
else{ 
   echo "User is not set"; 
exit; // Stop the entire process, it's not set
}

if (isset($_GET['key']) && (strlen($_GET['key']) == 32)){
  $key = $_GET['key'];
}
else{ 
   echo "Key is not set"; 
exit; // Stop the entire process, it's not set
}

Sidenote: Instead of isset(), you may be better off using !empty() instead.


My test codes:

Sidenote: You said you removed user_group = 'member' make sure that doesn't alter the process.

<?php 
$DB_HOST = 'xxx'; // Change those
$DB_USER = 'xxx'; // to your
$DB_PASS = 'xxx'; // own
$DB_NAME = 'xxx'; // credentials

$db = new mysqli($DB_HOST, $DB_USER, $DB_PASS, $DB_NAME);
if($db->connect_errno > 0) {
  die('Connection failed [' . $db->connect_error . ']');
}

$activation = md5(uniqid(rand(), true));

echo $activation . "<br>";

$user = "test";
$key = "b5bad6f02c247458e3d888f94b568819 "; 
// deliberate space added at the end of the string, remove it from this when testing.

$key = trim($key); // trims off the space at the end of the key string.

echo strlen($key); // yep, gotten 32

$sql = <<<SQL
  UPDATE users
  SET validation = NULL
  WHERE username='$user'
  AND validation='$key'

SQL;

$result = $db->query($sql);

if($result) {
  echo "Success" . "<br>"; 
// This does not always mean it was truly successful. Use affected_rows()
}

else{
  echo "Failed" . mysqli_error($db);
}

$affected = $db->affected_rows;

if($affected){
  echo "Affected yes";
}

else{
  echo "Not affected";
}

Should this not work for you, then I don't know what else I can say or do that will be of any further help; I'm sorry but I tried.

I sincerely wish you well and I hope this matter gets resolved.

Community
  • 1
  • 1
Funk Forty Niner
  • 74,450
  • 15
  • 68
  • 141
  • Okay so let me show you what I have. I went ahead and removed the user_group update. I can update that separately once I get the first update worked out. The original idea was to change validation in the db to NULL and user_group to 'member' (from validating) $sql = <<query($sql); $count = $result->affected_rows; Still no dice. – Miranda Roberts Jun 28 '15 at 03:10
  • @MirandaRoberts `affected_rows()` requires DB connection to it. See the manual http://php.net/manual/en/mysqli.affected-rows.php I.e.: `printf("Affected rows (SELECT): %d\n", $mysqli->affected_rows);` and `$mysqli` being the DB connection variable. So, in your case, that would be `printf("Affected rows (UPDATE for example): %d\n", $db->affected_rows);` – Funk Forty Niner Jun 28 '15 at 03:18
  • @MirandaRoberts so, don't use `$result` for affected rows to go with `$result = $db->query($sql);` - use `printf("Affected rows (UPDATE for example): %d\n", $db->affected_rows);` and check for errors using http://php.net/manual/en/mysqli.error.php – Funk Forty Niner Jun 28 '15 at 03:21
  • Oh! That makes sense. So in this case when used, it came up Affected rows (SELECT): 0. The key and user match up with what's in the db, so I'm still unsure what went wrong here. – Miranda Roberts Jun 28 '15 at 03:23
  • On a side-note, no errors came up using php.net/manual/en/mysqli.error.php. Just Affected rows (UPDATE for example): 0 – Miranda Roberts Jun 28 '15 at 03:26
  • @MirandaRoberts add error reporting as stated in my answer, and do `var_dump();` to see what's passing through or not. You can also pass variables or arrays through it, i.e. `var_dump($_GET['user']);` – Funk Forty Niner Jun 28 '15 at 03:26
  • @MirandaRoberts Try it without the heredoc syntax and see if it makes a difference: `$sql = "UPDATE users SET validation = NULL WHERE username='$user' AND validation='$key'";` - Plus, make sure the column lengths are long enough and of the correct type. That will fail silently sometimes. also try it without the affected rows – Funk Forty Niner Jun 28 '15 at 03:30
  • No change using the other syntax (but I do think I like it better then heredoc, I guess it was just the preference of the tutorial I read), and var_dumps came out fine and were proper length/type. – Miranda Roberts Jun 28 '15 at 03:34
  • @MirandaRoberts do `var_dump($sql);` right after your heredoc update code and show me the results. If nothing shows up, then something is failing. If it does show the user and the key, then your table/database is failing, and silently also. By "length", not just `strlen($_GET['key'])` if you thought that's what I meant, but the column lengths for the key, password fields etc. If you're off by only one/1, then that would do it. I really have to get some sleep, but I'll be back tomorrow to help you out more. I'll setup a db on my side but see what you can do in the meantime. – Funk Forty Niner Jun 28 '15 at 03:41
  • string(106) "UPDATE users SET validation = NULL WHERE username='test' AND validation='b5bad6f02c247458e3d888f94b568819'" is what I got. Thank you so much for your effort on this problem! You have no idea how much I appreciate it. I will comb over column lengths and anything else I can think of in the meantime. – Miranda Roberts Jun 28 '15 at 03:51
  • 1
    the user I'm playing with is just 'test' and each key is generated with the following code: $activation = md5(uniqid(rand(), true)); – Miranda Roberts Jun 28 '15 at 03:56
  • @MirandaRoberts Hi Miranda. I tested with success. Please reload my answer and look under **Edit:**. Please go over it very carefully. Good luck. – Funk Forty Niner Jun 28 '15 at 16:15