I've asked a few questions about this piece of code on this site.
Basically, my database is not updating when I use - $id = $GET_['id']; (at the top of the code below). The id gets passed to this page from the previous page - the url of the page is 'http:// www.21orange.com/CCC/changepassword.php?id=1'. There is an 'id' field in my database.
When I change the above line of code to - $id = '1' - the code runs perfectly and the database is updated. It only stops working when I use the $GET_['id']. Why is this?
// First we execute our common code to connection to the database and start the session
require("common.php");
$id = $_GET['id'];
// This if statement checks to determine whether the registration form has been submitted
// If it has, then the registration code is run, otherwise the form is displayed
if(!empty($_POST))
{
// Ensure that the user has entered a non-empty password
if(empty($_POST['password']))
{
die("Please enter a password.");
}
// Ensure that the user has entered a non-empty username
if(empty($_POST['confirmpassword']))
{
// Note that die() is generally a terrible way of handling user errors
// like this. It is much better to display the error with the form
// and allow the user to correct their mistake. However, that is an
// exercise for you to implement yourself.
die("Please confirm your password.");
}
if ($_POST['password'] == $_POST['confirmpassword']) {
// An INSERT query is used to add new rows to a database table.
// Again, we are using special tokens (technically called parameters) to
// protect against SQL injection attacks.
$query = "UPDATE Staff SET password=:password, salt=:salt WHERE id=:id";
// A salt is randomly generated here to protect again brute force attacks
// and rainbow table attacks. The following statement generates a hex
// representation of an 8 byte salt. Representing this in hex provides
// no additional security, but makes it easier for humans to read.
$salt = dechex(mt_rand(0, 2147483647)) . dechex(mt_rand(0, 2147483647));
// This hashes the password with the salt so that it can be stored securely
// in your database. The output of this next statement is a 64 byte hex
// string representing the 32 byte sha256 hash of the password. The original
// password cannot be recovered from the hash.
$password = hash('sha256', $_POST['password'] . $salt);
// Next we hash the hash value 65536 more times. The purpose of this is to
// protect against brute force attacks. Now an attacker must compute the hash 65537
// times for each guess they make against a password, whereas if the password
// were hashed only once the attacker would have been able to make 65537 different
// guesses in the same amount of time instead of only one.
for($round = 0; $round < 65536; $round++)
{
$password = hash('sha256', $password . $salt);
}
try
{
// Execute the query to create the user
$stmt = $db->prepare($query);
$stmt->execute(array(
'password' => $password,
'salt' => $salt,
'id' => $id));
}
catch(PDOException $ex)
{
// Note: On a production website, you should not output $ex->getMessage().
// It may provide an attacker with helpful information about your code.
die("Failed to run query: " . $ex->getMessage());
}
// This redirects the user back to the login page after they register
header("Location: stafflist.php");
// Calling die or exit after performing a redirect using the header function
// is critical. The rest of your PHP script will continue to execute and
// will be sent to the user if you do not die or exit.
die("Redirecting to stafflist.php");
}
die("Passwords do not match.");
}
I'm new to php, so forgive my naivety. P.s. I know the method I'm using is fairly old school, but it's only a test.
Thanks,
Joe
You can't do both GET and POST in a single HTTP Request.
However, you can use an hidden input field to get around this limitation:
In your HTML markup, you can add the following:
<input type="hidden" name="id"
value="<?php echo htmlspecialchars($_GET['id'], ENT_QUOTES); ?>" />
And your $_GET['id'] should work just fine.
To avoide this error
Undefined index: id in /home/content/47/11368447/html/CCC/changepassword.php on line 6
first test if the index exists :
if(isset($_GET['id'])) {
$id = $_GET['id'];
} else {
// here you can set a value for the id
}
Otherwise, you can add your $id var in your if test :
if(!empty($_POST) && $id)
{
//...
}
It looks like you are passing the 'id' to the action URL but for some reason the $_GET variable is not having it. Please double check:
Are you really passing the 'id' to the URL ? Please make sure.
Please check the code in common.php to see if the $_GET variable is modified in it.
Is that script behind a rewrite settings (e.g in .htaccess) ? if yes, $_GET parameters could be gone due to inappropriate rewrite settings. You can further test it out by putting print_r($_GET); in the beginning and visit that script directly ( GET instead of POST )
$id = $_GET['id'];
first check there is any value in $id to print the $id through echo
Related
Yes, I know there are answers for PHP MySQL update statements, however none of them have yet to solve the issue. My reset PHP file prepares the query just fine using bind_param or concatenated variables. It also executes fine as it redirects to the reset_success page. The database record however, goes unchanged. I have confirmed that the user the website is using has update privileges. I have even tried escaping password as it is a lowercase version of a reserve word, nothing has worked. The apache2 error log also shows no errors, so no help there.
The code is as follows:
if (empty($error_msg)) {
// Create a random salt
$random_salt = hash('sha512', uniqid(openssl_random_pseudo_bytes(16), TRUE));
// Create salted password
$password = hash('sha512', $password . $random_salt);
// Prepare the update statement
//if ($update_stmt = $mysqli->prepare("UPDATE members SET password = ?, salt = ? WHERE email = ?")) {
if ($update_stmt = $mysqli->prepare("UPDATE members SET password = '".$password."', salt = '".$random_salt."' WHERE email = '".$_SESSION['email']."';")) {
// Binding params
//$update_stmt->bind_param('sss', $password, $random_salt, $_SESSION['email']);
// Execute the update statement
if ($update_stmt->execute()) {
header('Location: reset_success.php');
exit();
}
else{
header('Location: error.php?err=Reset failure: UPDATE');
exit();
}
}
else{
header('Location: error.php?err=Reset failure: PREPARE');
exit();
}
}
Any insight would be greatly appreciated!
Ok, it is working. I needed an additional call to session_start() in the function file even though the reset_password.php does not redirect ($_SERVER['PHP_SELF']). This was NOT needed in for the registration page and its corresponding function file which operates in the same manner. I'm still unclear as to why that is needed again when the referrer is itself and is not redirecting.
This should be a comment, but its a bit long.
$random_salt = hash('sha512', uniqid(openssl_random_pseudo_bytes(16), TRUE));
This very wrong. You are padding the salt from 16 to 64 bits and removing the entropy by hashing it. If you want 64 random bytes then just use openssl_random_pseudo_bytes(64).
The apache2 error log also shows no errors
Have you checked that PHP error logging is working?
Why didn't you check the mysql logs?
if ($update_stmt->execute()) {
Try checking $update_stmt->affected_rows()
I've made my own login system based on a Wikihow article and I want to make a change password field, but I don't know how I should be doing it. Here is what I have now:
JavaScript
function formchange(form, current, newpass) {
// Create a new element input, this will be our hashed password field.
var p = document.createElement("input");
var p2 = document.createElement("input");
// Add the new element to our form.
form.appendChild(p);
p.name = "p";
p.type = "hidden";
p.value = hex_sha512(current.value);
form.appendChild(p2);
p.name = "p2";
p.type = "hidden";
p.value = hex_sha512(newpass.value);
// Make sure the plaintext password doesn't get sent.
current.value = "";
newpass.value = "";
// Finally submit the form.
form.submit();
}
PHP
if (isset($_POST['cpass'], $_POST['npass'])) {
$cpass = $_POST['cpass'];
$npass = $_POST['npass']; // The hashed password.
echo "Here";
$un = getUser();
if ($cpass == $npass) {
echo "If";
$random_salt = hash('sha512', uniqid(mt_rand(1, mt_getrandmax()), true));
$password = hash('sha512', $npass . $random_salt);
// Insert the new user into the database
if ($insert_stmt = $mysqli->prepare("MYSQL UPDATE STATEMENT")) {
$insert_stmt->bind_param('p', $password);
$insert_stmt->bind_param('s', $random_salt);
echo "Binded";
// Execute the prepared query.
if (! $insert_stmt->execute()) {
header('Location: ../home.php?msg=1');
}
}
header('Location: ../home.php?msg=2');
} else {
// Failed
header('Location: ../?error=1');
}
} else {
// The correct POST variables were not sent to this page.
echo 'Invalid Request';
}
It doesn't seem to work, it keeps going to home.php?msg=2, but the database isn't updated. I'm new to all of this, and it's one of my projects to learn it so apologies for terrible code. My MySQL statement is this:
UPDATE user_data SET user_data.password = 'p', user_data.salt = 's' WHERE username = '$un';
Your script is set up to forward to home.php?msg=2 for many conditions, including when the query succeeds.
if (! $insert_stmt->execute()) {
header('Location: ../home.php?msg=1');
}
This will send it to home.php?msg=1 if the query fails to execute, note the !. Since the query doesn't fail, the script continues and the next function is...
header('Location: ../home.php?msg=2');
"It doesnt seem to work, it keeps going to home.php?msg=2. "
So what do you expect the script to do? How you've designed it, it is supposed to do this.
Update on new edit
You should avoid using header location like this, otherwise you'll never be able to find your errors. You should avoid using header location at all during development, especially when a query fails.
I would enclose header location more and grab the error to see why the statement failed. I don't ever use mysqli because pdo, in my opinion, is better but I think this should work.
if ($insert_stmt = $mysqli->prepare("MYSQL UPDATE STATEMENT")) {
$insert_stmt->bind_param('p', $password);
$insert_stmt->bind_param('s', $random_salt);
echo "Binded";
// Execute the prepared query.
if (! $insert_stmt->execute()) {
die($mysqli->error);
// header('Location: ../home.php?msg=1');
}
else {
// Success, forward
header('Location: ../home.php?msg=2');
}
}
else {
die($mysqli->error);
}
Removing the header('Location: ../home.php?msg=2'); at the bottom of this section in your original script.
What you are trying to do is fundamentally flawed. An attacker could still perform a replay attack with the hash values created by your JavaScript function. Also you are only protecting the un-hashed password (but I can still look it up in a rainbow table) not the account. An attacker can use the hash values to authenticate as the user. This is because the hash values are the password.
To actually make a secure form you will need to use HTTPS and a Cross site request forgery token of some kind.
Here is an example say my name is Alice, and I am watching Bob's network traffic.
I (as Alice) see Bob submit your form, and then I see your web server respond back to Bob saying "thanks, password updated"
I (as Alice) now know Bob's password, even worse I can now send the same HTTP request Bob just sent you but now since I know Bob's password I can change the request to update the password again, to anything I want.
Your form is no more secure than sending the password un-hashed.
I'm using the following code to update the password and salt fields in my database :
// First we execute our common code to connection to the database and start the session
require("common.php");
$id = $_GET[id];
// This if statement checks to determine whether the registration form has been submitted
// If it has, then the registration code is run, otherwise the form is displayed
if(!empty($_POST))
{
// Ensure that the user has entered a non-empty password
if(empty($_POST['password']))
{
die("Please enter a password.");
}
// Ensure that the user has entered a non-empty username
if(empty($_POST['confirmpassword']))
{
// Note that die() is generally a terrible way of handling user errors
// like this. It is much better to display the error with the form
// and allow the user to correct their mistake. However, that is an
// exercise for you to implement yourself.
die("Please confirm your password.");
}
if ($_POST["password"] == $_POST["confirmpassword"]) {
// An INSERT query is used to add new rows to a database table.
// Again, we are using special tokens (technically called parameters) to
// protect against SQL injection attacks.
$query = "UPDATE Staff SET password=:password, salt=:salt WHERE id=:id";
// A salt is randomly generated here to protect again brute force attacks
// and rainbow table attacks. The following statement generates a hex
// representation of an 8 byte salt. Representing this in hex provides
// no additional security, but makes it easier for humans to read.
$salt = dechex(mt_rand(0, 2147483647)) . dechex(mt_rand(0, 2147483647));
// This hashes the password with the salt so that it can be stored securely
// in your database. The output of this next statement is a 64 byte hex
// string representing the 32 byte sha256 hash of the password. The original
// password cannot be recovered from the hash.
$password = hash('sha256', $_POST['password'] . $salt);
// Next we hash the hash value 65536 more times. The purpose of this is to
// protect against brute force attacks. Now an attacker must compute the hash 65537
// times for each guess they make against a password, whereas if the password
// were hashed only once the attacker would have been able to make 65537 different
// guesses in the same amount of time instead of only one.
for($round = 0; $round < 65536; $round++)
{
$password = hash('sha256', $password . $salt);
}
try
{
// Execute the query to create the user
$stmt = $db->prepare($query);
$stmt->execute(array(
':password' => $password,
':salt' => $salt,
':id' => $id));
}
catch(PDOException $ex)
{
// Note: On a production website, you should not output $ex->getMessage().
// It may provide an attacker with helpful information about your code.
die("Failed to run query: " . $ex->getMessage());
}
// This redirects the user back to the login page after they register
header("Location: login.php");
}
die("Passwords do not match.");
}
There is an 'id' field in the database, and a member of staff with the id equal to 1 (the link on the previous page passes the id to this page, in this example the id would be 1). I'm not sure why it is not updating the database. I'm new to php and would love any help.
Thanks,
Joe
Incorrect syntax, you want to call the $id using:
$id = $_GET['id'];
I think when you do execute(array)blah it treats all variables as string,so use
http://www.php.net/manual/en/pdostatement.bindparam.php
$stmt ->bindParam(':password', $password, PDO::PARAM_STR)
$stmt ->bindParam(':salt', $salt, PDO::PARAM_STR)
$stmt ->bindParam(':id', $id, PDO::PARAM_INT)
$stmt ->execute();
I basically receive the following error when running the below piece of code:
Failed to run query: SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens
I'm new to php and am not sure why I am receiving this error... I have used this piece of code for an INSERT (not UPDATE) statement in the past and it worked fine.
The code has been copied from another site, and is simply being used as a test...
// First we execute our common code to connection to the database and start the session
require("common.php");
$id = $_GET[id];
// This if statement checks to determine whether the registration form has been submitted
// If it has, then the registration code is run, otherwise the form is displayed
if(!empty($_POST))
{
// Ensure that the user has entered a non-empty password
if(empty($_POST['password']))
{
die("Please enter a password.");
}
// Ensure that the user has entered a non-empty username
if(empty($_POST['confirmpassword']))
{
// Note that die() is generally a terrible way of handling user errors
// like this. It is much better to display the error with the form
// and allow the user to correct their mistake. However, that is an
// exercise for you to implement yourself.
die("Please confirm your password.");
}
if ($_POST["password"] == $_POST["confirmpassword"]) {
// An INSERT query is used to add new rows to a database table.
// Again, we are using special tokens (technically called parameters) to
// protect against SQL injection attacks.
$query = "UPDATE Staff SET password=:password, salt=:salt WHERE id=:id";
// A salt is randomly generated here to protect again brute force attacks
// and rainbow table attacks. The following statement generates a hex
// representation of an 8 byte salt. Representing this in hex provides
// no additional security, but makes it easier for humans to read.
$salt = dechex(mt_rand(0, 2147483647)) . dechex(mt_rand(0, 2147483647));
// This hashes the password with the salt so that it can be stored securely
// in your database. The output of this next statement is a 64 byte hex
// string representing the 32 byte sha256 hash of the password. The original
// password cannot be recovered from the hash.
$password = hash('sha256', $_POST['password'] . $salt);
// Next we hash the hash value 65536 more times. The purpose of this is to
// protect against brute force attacks. Now an attacker must compute the hash 65537
// times for each guess they make against a password, whereas if the password
// were hashed only once the attacker would have been able to make 65537 different
// guesses in the same amount of time instead of only one.
for($round = 0; $round < 65536; $round++)
{
$password = hash('sha256', $password . $salt);
}
try
{
// Execute the query to create the user
$stmt = $db->prepare($query);
$stmt->execute(array(
':password' => $password,
':salt' => $salt));
}
catch(PDOException $ex)
{
// Note: On a production website, you should not output $ex->getMessage().
// It may provide an attacker with helpful information about your code.
die("Failed to run query: " . $ex->getMessage());
}
// This redirects the user back to the login page after they register
header("Location: login.php");
// Calling die or exit after performing a redirect using the header function
// is critical. The rest of your PHP script will continue to execute and
// will be sent to the user if you do not die or exit.
die("Redirecting to login.php");
}
}
Thanks a lot,
Joe
You missing the :id parameter:
$stmt->execute(array(
':password' => $password,
':salt' => $salt,
':id' => $id
));
You forgot to pass in the id
$stmt = $db->prepare($query);
$stmt->execute(array(
':password' => $password,
':salt' => $salt,
':id' => $id));
I've cribbed this code almost verbatim from a bunch of very helpful answers here on SO, so I can't get my head around what's wrong.
First, here's my function for creating a user account:
function BFcrypt($password,$cost)
{
$chars='./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$salt=sprintf('$2a$%02d$',$cost);
for($i=0;$i<22;$i++) $salt.=$chars[rand(0,63)];
return array(
'salt'=>$salt,
'hash'=>crypt($password,$salt)
);
}
Then, when a user goes to login:
case 'login':
$login =$_POST['login'];
$pwd =$_POST['pwd'];
$sql ="SELECT * FROM `users` WHERE `users`.`login`='$login' LIMIT 1;";
if($query = mysql_query($sql)){
$row=mysql_fetch_assoc($query);
print_r($_POST);
print_r($row);
$hash = $row['password'];
if(crypt($pwd,$hash)==$hash){
echo"SUCCESS";
}else{
echo"FAILURE";
}
}
The login function appears to always be failing. I've set it to show me $pwd, $hash and crypt($pwd,$hash), and for some reason, crypt($pwd,$hash) never seems to == $hash.
Here's a row in the database for a sample user (I'm logging the salt now, though I know it's supposed to be included in the hash:
'id'=>'680',
'login'=>'argh',
'password'=>'$2a$10$BWZAX7wrwQp5iyK4kh6VLunqy82eiXg7GaDs6mJLqdgT5s2qiUqYW',
'salt'=>'$2a$10$BWZAX7wrwQp5iyK4kh6VL5',
'first'=>'argh',
'last'=>'argh',
'zip'=>'00000',
'email'=>'argh',
'date updated'=>'2012-12-12 16:05:29'
I believe that when I call crypt($pwd,$hash),it truncates $hash, leaving only the original 22-character salt (plus prefix), thus the output will be the same as $hash as long as $pwd is the same. I'm seeing clearly there's an issue here in that the salt I'm recording is one character longer than the one that ends up appended to the hash, but it's the appropriate length for blowfish, and anyway, making it one character shorter doesn't seem to help.
I can't figure out what I'm doing wrong here. Any help would be appreciated.
Based on your own salt value and password of 'argh' I ran a small test script:
$hash = crypt('argh', '$2a$10$BWZAX7wrwQp5iyK4kh6VL5');
// $2a$10$BWZAX7wrwQp5iyK4kh6VLuIzJHihvZTdfpRXNkTPVKkTiGfLDl1RO
var_dump(crypt('argh', $hash) == $hash);
// bool(true)
The problem doesn't seem to be in the code you've shown.
You could check your database field width to store the password hash, which should be at least 60 wide. And while you're at it, fix your SQL injection vulnerability (by using prepared statements most preferably).