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.
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 have been trying to get a page working for a number of days now, and there doesn't seem to be much help from the "related" questions on this site.
I have made a signup.php page, which has a form for inputting user credentials to signup up for the site I am building, when the form is filled out and the user presses the 'submit' button, the form uses the action "signupsuccess.php" which has all of the php code for inserting the credentials into the database, and then redirects the user to the "Login.php" page.
My problem:
I have written code to say that if the user has not put in any data for one of the fields in the form, then they are brought back to the signup.php page by using this code:
<?php
if(!isset($_POST['fname'])&&($_POST['lname'])&&($_POST['email'])&&($_POST['pass'])){
header('Location:Signup.php');
exit;
}
else{
$host = "localhost";
$user = "******";
$password = "******";
$conn = mysql_connect($host, $user, $password);
$db = mysql_select_db('*****', $conn);
if(! get_magic_quotes_gpc() )
{
$fname = addslashes ($_POST['fname']);
$lname = addslashes ($_POST['lname']);
$email = addslashes($_POST['email']);
$pass = addslashes($_POST['pass']);
}
else
{
$fname = $_POST['fname'];
$lname = $_POST['lname'];
$email = $_POST['email'];
$pass= $_POST['pass'];
}
$query = mysql_query("select * from users where pass='$pass' AND email='$email'", $conn);
$rows = mysql_num_rows($query);
if ($rows == 1) {
$errors[] = 'That user already exists, try another email';
}else
{
$sql = "INSERT INTO users ".
"(fname,lname, pass, email) ".
"VALUES('$fname','$lname','$pass','$email')";
$retval = mysql_query( $sql, $conn );
if(! $retval )
{
die('Could not enter data: ' . mysql_error());
}
}
}
mysql_close($conn);
?>
But the header() just won't bring the user back when they haven't put anything in to the fields. Is there anything I am doing obviously wrong or can anyone help me sort out the redirection of the user if they haven't entered anything.
Your if statement is incorrect. If you're just trying to check to see if those variables are set you need to call isset() on all of them.
You can do this with individual calls to isset() or all in one call.
if(!isset($_POST['fname'],$_POST['lname'],$_POST['email'],$_POST['pass'])){
header('Location:Signup.php');
exit;
}
FYI, you are wide open to SQL injections. addslashes() does not prevent SQL injections. Also, the mysql_* funcstions are obsolete and you should not be writing new code using them. Look into mysqli or PDO instead.
I don't think you really need a redirection, you probably should overcome this issue from the frontend, maybe a js validation could do the trick and is way simpler.
1.- change the action of submit to run the function "validate()"
2.- create the function that will be something like:
$(document).ready(function(){
function validate(){
if ($.trim($("#inputid").val()) == ""){
$(this).css('border', '2px solid red');
} else if ($.trim($("#inputid2").val()) == "") {
$(this).css('border', '2px solid red');
} else if ($.trim($("#inputid3").val()) == "") {
$(this).css('border', '2px solid red');
} else {
submit();
}
}
});
Where '#input?' is the selector for the input you want to validate and null is the value that you want to avoid, in this case, no value, just empty input. Then if all the inputs are filled it will execute submit() function which you should create to do whatever he has to.
Note: This kind of selectors are for jquery so you must include it in your code as well, put this in your header
<script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
Note 2: This is the frontend approach. I don't know if this is convenient but at least is an option and helps.
Good luck!
Your if statement was the problem. because the isset was only affecting
$_POST['fname'],
the if statment was being skipped so
header('Location:Signup.php')
was not being reached.
I like to put
echo 'test';
in my code while i am testing it and move it around the code. That way, if it is not echoing 'test', i know that the code isn't even being reached. That could have helped you in this case, showing you that the problem wasn't the header, it was the if statement. Also, consider using PDO for mysql connections. It is more secure against mysql injections.
Your condition (if corrected according to the previous answers) would still always result in the else case. Since you are checking for $_POST fields, those will always be present. isset()returns false if the variable is not set (but it is: it comes from your form) or is NULL (which it is not: it contains an empty value). So, isset() will return true fopr every field. What you need is, for each field: if (empty(trim($_POST['fname']))) || ... )empty() returns false when the variable is not set or empty (i.e NULL, an empty string, 0, 0.0, false, etc, see here: http://php.net/manual/en/function.empty.php)Plus, you need to do something about the deprecated mysql_functions and your vulnerability to attacks.
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
I'm getting this error and I don't quite understand why. I've been going over this for hours now, tried looking into it via research, no luck.
In my PHP login system, I check if the row is selected:
//Start session
session_start();
//Include database connection details
require_once('config.php');
//Array to store validation errors
$errmsg_arr = array();
//Validation error flag
$errflag = false;
//Connect to mysql server
$link = mysql_connect(DB_HOST, DB_USER, DB_PASSWORD);
if(!$link) {
die('Failed to connect to server: ' . mysql_error());
}
//Select database
$db = mysql_select_db(DB_DATABASE);
if(!$db) {
die("Unable to select database");
}
//Prevent SQL injection.
function clean($str) {
$str = #trim($str);
if(get_magic_quotes_gpc()) {
$str = stripslashes($str);
}
return mysql_real_escape_string($str);
}
//Sanitize the POST values
$login = clean($_POST['login']);
$password = clean($_POST['password']);
//Input Validations
if($login == '') {
$errmsg_arr[] = 'Login ID missing';
$errflag = true;
}
if($password == '') {
$errmsg_arr[] = 'Password missing';
$errflag = true;
}
//If there are input validations, redirect back to the login form
if($errflag) {
$_SESSION['ERRMSG_ARR'] = $errmsg_arr;
session_write_close();
echo "input validation";
exit();
}
//Create query
$qry="SELECT * FROM details WHERE USERNAME='$login' AND PASSWORD='".md5($_POST['password'])."'";
$result=mysql_query($qry);
//Check whether the query was successful or not
if($result) {
if(mysql_num_rows($result) == 1) {
//Login Successful
session_regenerate_id();
$member = mysql_fetch_assoc($result);
$_SESSION['MEMBER_ID'] = $member['USERNAME'];
session_write_close();
header("location: client-index.php");
exit();
}else {
//Login failed
echo "login failed?";
exit();
}
}else {
die("Query failed");
}
?>
It echos "failed", for whatever reason.
If you told us what the error is, it might help us answer your question better.
But at any rate, this is emphatically the WRONG way to go about this. There are many serious problems with this system.
First of all, you're not sanitizing your inputs. Since you're mixing your data and commands together, all a user would have to do is enter a username of "x' or 1 = 1' --" to get into the system. Here's why: the only command the SQL server would get is "SELECT * FROM details WHERE USERNAME = 'x' or 1 = 1". In other words, if the username is x or 1 = 1 (which it is), then the SQL server would respond with a positive result. (The two dashes at the end of the "username" denote a comment in SQL, so everything after that in the query would be ignored).
A truly malicious attacker could even wreak havoc on your system by entering a username "x'--; DROP TABLES;', and your entire database would be gone. (See this comic for where I got this.)
In fact, you shouldn't even really be using mysql_query at all. According to the PHP documentation:
Use of this extension is discouraged. Instead, the MySQLi or PDO_MySQL extension should be used.
I would do a bit more reading on the subject if I were you. Even if this is just for practice, it's still best to get things right the first time. Look into PDO: it's not too hard to learn, yet quite useful. Its main advantage is that it does not mix data and commands, so you won't have the same problem of unsanitized inputs messing up your database.
Also, while it's good to see that you're hashing your passwords--and you'd be amazed at how many companies that should know better do not--MD5 is no longer considered cryptographically secure. It's relatively easy to get what's called a "hash collision," where two different plaintexts produce the same hash. Now, SHA-256 should be the minimum you use.
Also, on the subject of hashing, you should be adding something called salt. A salt is some kind of random text that you add to your plaintext in order to further obfuscate it. The reason for this is that there are what's called rainbow tables out there. A rainbow table is a list of pre-calculated hashes of all common passwords. If someone were to get a hold of your database, they could then compare all the passwords to rainbow tables to find their plaintexts.
Finally, in order to slow down brute force attacks--where an attacker tries all alphanumeric combinations until they get the password--you should also be using a loop where the hash algorithm gets re-calculated x number of times, usually between 1000 and 10000 times. PHP's crypt does this very nicely.
And BTW: don't feel bad. I've done all these things before, too. That's why I know that you shouldn't do them. Don't worry--you'll get there soon enough. Keep at it!
I am new to site so can not add comments maybe this wont help much but ill give it a go anyway
in your sql query it looks like you passing variable $login as text not variable value
$qry="SELECT * FROM details WHERE USERNAME='$login' AND PASSWORD='".md5($_POST['password'])."'";
and it should be
$qry="SELECT * FROM details WHERE USERNAME=".$login." AND PASSWORD='".md5($_POST['password'])."'";
is it a query failed or login failed?
anyway if query failed :
try to change your query in to this :
surrounds your fields with backtick (not single quote)
$qry="SELECT * FROM details WHERE `USERNAME`='$login' AND `PASSWORD`='".md5($_POST['password'])."'";
if login failed :
if(mysql_num_rows($result) == 1) {
//Login successful
}else {
//Login failed
}
are you sure that the query will only have 1 result? because with this condition, if results is greater than 1 it will also failed to login.
I've done quite a bit of research and preparation with my code to try and prevent SQL injections, but I wanted to discuss something that I'm not quite sure about.
I understand the the mysqli_real_escape_string does not escape _ (underscore) and % (percent) characters. If I'm not using any LIKE clauses in my SQL statements, does this open me up to any risk?
Below is an example of one the instances I'm interested in talking about. Here is the login script I'm using. I want to make sure that I'm not opening myself up to any injection vulnerabilities here. Your insight and feedback would be greatly appreciated.
// Initiate login process if the mode is set to login
if ($_REQUEST['mode'] == "login") {
// Open shared database connection
$cxn = connectDb();
// Escape characters to help prevent a SQL injection attack
$username = mysqli_real_escape_string($cxn, $_POST['user']);
// Convert submitted password to hashed value using
// custom password hashing function
$password = custompwhash($_POST['pass']);
// Execute SQL statement to determine if the credentials provided
// match a valid user
$sql = "SELECT count(*) as countOK FROM user_def WHERE ".
"username = '$username' AND password = '$password'";
$result = mysqli_query($cxn,$sql);
$row = mysqli_fetch_array($result);
extract($row);
// If the username value submitted is null, throw and error
if ($username == "") {
die2("Please enter your username and try again.<br />");
failedloginalert($username);
}
// If the password value submitted is null, throw and error
else if ($password == "") {
die2("Please enter your password and try again.<br />");
failedloginalert($username);
}
// If the credenetials provided match a valid user in the database,
// initate login
else if ($countOK == '1') {
$sql2 = "INSERT INTO `user_activity` (`username`, `time`, `ip`)".
" VALUES ('$username', NOW(), '{$_SERVER['REMOTE_ADDR']}')";
$result2 = mysqli_query($cxn,$sql2);
$_SESSION['auth'] = 1;
$_SESSION['username'] = $username;
// If the user does not need to be directed to a specific page, direct to home.php
if (empty($_GET['page'])) {
die2("<span style='color:#000;'>You have successfully logged in. <br /><br />
Please click here if you are not automatically redirected.</span>
<meta http-equiv='refresh' content='0;url=home.php'/>");
die();
}
// Otherwise, if the user does need to be directed to a specific page, direct to the requested page
else {
$loginredirectpage = $_GET['page'];
die2("<span style='color:#000;'>You have successfully logged in. <br /><br />
Please click here if you are not automatically redirected.</span>
<meta http-equiv='refresh' content='0;url=".$loginredirectpage."'/>");
die();
}
}
// Since the credenetials provided do not match a valid user in the database, throw an error
else {
die2("The username or password you entered is invalid. Please try again. <br/><br/>If the problem persists, reset your password.");
failedloginalert($username);
}
}
Start using PDO and Prepared Statements and your issues with SQL injection will go away.
If I'm not using any LIKE clauses in
my SQL statements, does this open me
up to any risk?
No it doesn't. = just gives you more exact answer than LIKE
SQL Injection is more to do with quote sign ' " since the technique tries to append on the query string. So I'd say % and _ won't open up any security risk if you filtered all the possible quotes. In this case, mysqli_real_escape_string() can help.
PDO is an abstraction layer that assists you with dealing with databases more efficiently. It can help you with SQL injection, but I don't recommend using it unless you're building something big that requires a lot of interaction with database.
So final my point is your code looks ok (: