pals, got a quite disgusting situation here with my submit form. So, the problem: once user submits form his data goes to database and THEN if he refreshes the page the form submits again and he can do it infinite times and my DB will be full of useless data. So, how to prevent form submition after f5? I tried to use header('Location: success.php'); , but it doesn't help. Here is my server.php code:
<?php
session_start();
$message = "Wrong input";
$username = "";
$email = "";
$errors = array();
$db = mysqli_connect('localhost', 'root', 'root', 'example');
if(isset($_POST['register'])) {
$username = mysqli_real_escape_string($db, $_POST['username']);
$email = mysqli_real_escape_string($db, $_POST['email']);
$password = mysqli_real_escape_string($db, $_POST['password']);
if(empty($username) || empty($email) || empty($password)) {
echo "<script type='text/javascript'> alert('$message');</script>";
array_push($errors, "err");
}
}
if(count($errors) == 0) {
$pass = md5($password);
$query = "INSERT INTO users (username, email, password) VALUES ('$username', '$email', '$pass')";
mysqli_query($db, $query);
header('Location: success.php');
}
?>
f5 will just send the last request again, so you can't stop it
if you want to prevent that, you should add some test before creating a new user (check if the user is new or not, validate email...) and storing it inside the db
and change your md5 hash for the password. you should use a salt + sha* hash solution. you should considere to update your code with preparedStatement too
The reason you're getting junk data here is because you have zero validation logic. Every application must do at least some sort of superficial parameter cleanup, such as removing extraneous spaces, forcing lower or upper case for certain fields, and stripping any unwanted characters.
Then you'll need to check that the required fields are present, and are in a form that's acceptable. For example, an email address must contain at least an #, a name must contain at least one letter, and so on. Different field types have different requirements, where generally passwords have the most constraints, as you might need to enforce minimum/maximum lengths, presence of capital letters and/or numbers, and other such considerations.
If and only if the data's passed that scruitiny do you move on to the creation phase where applications must do is verify uniqueness of certain fields. This usually involves a quick check of the form:
SELECT COUNT(*) FROM users WHERE username=?
Note that this check is considered advisory only, that the information gleaned here is immediately considered obsolete. It can be used to present information to the user in the form of errors. It cannot be trusted going forward in the code, that is, your database can and will change after that statement is executed, rendering any tests invalid.
The third step is to commit the record, actually insert it. To prevent duplication at this point you'll need to have a UNIQUE constraint on any fields that must be unique. You'll also need to have code that captures these errors and re-surfaces them to the user in a form they can understand.
If this sounds like a lot of work, it's because it is. Don't write all this code by hand, it's a huge waste of time and you will get it wrong. Use a development framework to give you the foundation for this sort of thing. They come in a variety of flavours, from very light-weight like Fat-Free Framework to extremely full-featured like Laravel and all shades between. These implement everything I've talked about in different ways, the syntax and methodology can vary considerably, but the principles are the same.
Find one that you like, learn it well, and follow their community best-practices.
Related
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 2 years ago.
Improve this question
this is my first time posting on stackoverflow for a couple of years, so please excuse me if this isn't the correct posting format.
I'm trying to create a simple php registration and login script using mysqli. I have not yet added prepared statements to protect myself from SQL injection attacks as I want to learn the basics of submitting and receiving data from a mysql database first.
I have created a registration script that uses the password_hash function. This is completely working and is submitting to my mysql database perfectly.
However, i'm having problems with the password_verify function. I cannot get it to seem to work. If i don't use the password_hash or password_verify function in my register.php or login.php, the login works perfectly.
Although when using password_verify and password_hash together i cannot get the code to work.
Here's the code for my login.php:
<?php
include('database_connect.php');
if(isset($_POST["submit"])) {
$email = $_POST['email'];
$password = $_POST['password'];
$query = mysqli_query($conn, "SELECT * FROM users WHERE email='$email'");
$row = mysqli_fetch_array($query);
$encrypted_password = $row['password'];
if($row == 1) {
if(password_verify($password, $encrypted_password)) {
session_start();
$_SESSION['email'] = $email;
header("Location: index.php");
exit;
} else {
echo "Incorrect email or password";
}
} else {
echo "Email cannot be found, please sign up for an account";
}
}
?>
Here is the code for my register.php:
<?php
include('database_connect.php');
if(isset($_POST["submit"])) {
$firstName = $_POST['firstName'];
$lastName = $_POST['lastName'];
$email = $_POST['email'];
$password = $_POST['password'];
$confirmPassword = $_POST['confirm_password'];
$encrypted_password = password_hash($password, PASSWORD_DEFAULT);
$sql = "INSERT INTO users (firstName, lastName, email, password) VALUES ('$firstName','$lastName', '$email', '$encrypted_password')";
if ($conn->query($sql)) {
echo "<span>Account created succesfully, please <a href='login.php'>log in</a></span>";
} else {
echo "<span>There was an error, please contact site administrator</span>";
}
}
?>
I have removed the registration form validation so that the code is easier to read. If anybody could point me to the right direction or help me out, i would gladly appreciate it.
I'm pretty certain it has something to do with password hash, but i'm not sure and after countless attempts i cannot get it working.
All the best.
(in no particular order)
1)
$encrypted_password = password_hash('$password', PASSWORD_DEFAULT);
'$password' is a literal $password, because it is in single quotes PHP ignores the variable identifier ($) and prints it directly, as-is.
So your password value is always going to be "$password" and not the string that you were given in $_POST['password'].
Read about this on the original Q&A
2)
$row = mysqli_num_rows($query);
This variable is a numeric counter of rows, it is not the data within the rows.
To get the data within the rows you want mysqli_fetch_array($query) (or its similar companions).
So;
$row = mysqli_fetch_array($query);
This will be an array of data values from the database.
3)
Your code is wide open to SQL Injection attack and is completely unsafe. Yes, you're using password hash; that's a start, but you're not securing your user submitted data.
You should be using either MySQLi or PDO Prepared Statement RIGHT NOW.
4)
Headers should always be followed by die/exit commands to stop PHP continuing to execute script actions before deciding to follow the header() relocation action.
e.g.
header("Location: home.php");
exit;
5)
Whilst you may technically survive this time, on this page, you should be putting session_start(); at the very top of your PHP page, not half way down nestled in an unrelated if statement. this is inconsistent and very poor coding that will come back to bite you in the arse.
6)
Look at number 3 again, above, and absolutely do it!
7)
NEVER TRUST USER DATA. EVER.
So check and verify all values passwed from the <form> are roughly the expected shape. (filter_var($email, filter_validate_email) ;-)
After update to question.
8)
Read your PHP Error Log file. This will save you (and us all) an epic amount of time!
9)
Best practise is that if you're using PASSWORD_BCRYPT or PASSWORD_DEFAULT that you really, really should be manually setting the cost parameter yourself as the default value (10) is far too low. You should be setting it to as high a value as your server can comfortably take (14 as a guideline minimum).
Please view the details on the PHP Password_hash Manual page.
$encrypted = password_hash($password, PASSWORD_DEFAULT, ['cost' => 15]);
I am attempting to create a register page and am having trouble trying to hash my password using PASSWORD_BCRYPT with mysqli. Can someone explain what is wrong with my code below?
<?php
if(isset($_POST['Register'])) { // checking that form is submitted
session_start(); //creating variables for form entries
$FName = $_POST['First_Name'];
$LName = $_POST['Last_Name'];
$Email = $_POST['Email'];
$PW = $_POST['Password'];
$StorePassword = password_hash($PW, PASSWORD_BCRYPT, array('cost' => 10));
$sql = $con->query("INSERT INTO users (Fname, Lname, Email, Password)Values('{$FName}', '{$LName}', '{$Email}', '{$PW}')");
header('Location: Login.php');
Firstly, you're using the wrong variable for the password in the query being $PW rather than the intended $StorePassword variable where you're using it on top, then passing it to the hashing function.
Your password is being stored as "rasmuslerdorf" rather than "$2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a"
Example pulled from the manual.
If that still doesn't work then that function may not be available for you to use and will need to use the password compatibility pack
(if PHP < 5.5) https://github.com/ircmaxell/password_compat/
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: Displaying errors should only be done in staging, and never production.
Consult these following links
http://php.net/manual/en/mysqli.error.php
http://php.net/manual/en/function.error-reporting.php
and apply that to your code.
You may have errors in your query but you're not checking for them.
Plus, seeing you did not post your HTML form, make sure it is using a POST method and that all inputs bear the proper name attributes.
Just for argument's sake; your posted code is missing a closing brace }
Also add exit; after header, should there be more code after that. Otherwise, your code may want to continue to execute.
Make sure you are indeed successfully connected using the same MySQL API as you are using for querying, being mysqli_. That is unknownst to us.
Different APIs such as mysql_ and PDO do not intermix with mysqli_ and vice-versa.
Make sure you're not outputting before header using session_start(); in the place it's in now; it looks as if there's a space before your opening PHP tag, that is considered as output. Error reporting will tell you that also.
Your present code is open to SQL injection. Use prepared statements, or PDO with prepared statements, they're much safer.
Footnotes:
Make sure that the password column is long enough to store the hash. PHP.net recommends using VARCHAR(255) and in order to accomodate for the future. Same thing for all columns and of the correct lengths/types.
http://php.net/manual/en/function.password-hash.php
"Note that this constant is designed to change over time as new and stronger algorithms are added to PHP. For that reason, the length of the result from using this identifier can change over time. Therefore, it is recommended to store the result in a database column that can expand beyond 60 characters (255 characters would be a good choice)."
I need help with my login-script - it seems to be broken. If I enter no password I still get logged in correctly. Also if I don't enter anything. But if I enter the wrong username AND password it says my login credentials were wrong.
<?php
$verbindung = mysql_connect("localhost", "root" , "")
or die("Verbindung zur Datenbank konnte nicht hergestellt werden");
mysql_select_db("v1nce_website") or die ("Datenbank konnte nicht ausgewählt werden");
$username = $_POST["username"];
$password = $_POST["password"];
$abfrage = "SELECT username, password FROM logins WHERE username='$username' LIMIT 1";
$ergebnis = mysql_query($abfrage);
$row = mysql_fetch_object($ergebnis);
if($row->password == $password)
{
$_SESSION["username"] = $username;
echo "<p>Login erfolgreich.</p>";
}
else
{
echo "<p>Benutzername oder Passwort waren falsch. Login</p>";
}
?>
Any help would be appreciated.
There's a lot of things going on in this script that are worrisome:
Let's start with the actual problem you are here for. If your query won't find a matching row, $row will equal false. And since therefore, $row is not an object, $row->password will evaluate to NULL. And so, if $password is an empty string $row->password == $password will evaluate to true, because NULL == "" is truthy.
You would have been notified of this, had you turned on the displaying of errors, for instance with ini_set( 'display_errors', true );, in combination with a sufficient error reporting level, error_reporting( E_ALL ); for instance.
When you enter a wrong username and a wrong password, $row->password will again be NULL, but since you entered a non-empty string for a password, this time $row->password == $password will evaluate to false.
So, to mitigate this problem you need to make certain that you first check that there is actually a matching row, before you start comparing the passwords, for instance by evaluating mysql_num_rows( $ergebnis ) first.
Your script is vulnerable to SQL injection. This means that users of your script could potentially do harm when they enter SQL hacks as values for $_POST[ 'username' ]. For instance if I were to enter ' OR 1 = 1 -- your SQL query would result in the following (formatted for display purposes):
SELECT username, password
FROM logins
WHERE username='' OR 1 = 1 --' LIMIT 1
... always resulting in at least one row if the table is non-empty, because WHERE username='' OR 1 = 1 always evaluates to true (-- in SQL signifies a comment, so ' LIMIT 1 won't even be evaluated anymore).
To mitigate this problem, in your current setup, you need to sanitize your input values first with mysql_real_escape_string(), before passing them into the SQL query, like this:
$username = mysql_real_escape_string( $_POST["username"] );
But as others have advised already as well, you'd be wiser to start using a MySQL compliant library that offers prepared statements with parametrized queries, such as PDO or MySQLi, since the mysql_* library is in the process of being deprecated, because it offers poor means of defending against SQL injection.
Your passwords are stored verbatim (as plain text) in the database. This offers a variety of potential risks of accounts (and possibly user-related accounts) being compromised. Anyone who has access to the database (be it direct, authorized, access, or when the database is compromised) can view the passwords in clear text, and could therefore use these to either log in to your site, or use it as a potential login for other sites and/or services. After all, it is not uncommon for people to use the same combination of username and password for a variety of other sites and services.
To mitigate this problem you'd be wise to hash (one-way encrypt) the passwords before storing them in the database, and then, when the user wants to log in, compare the stored hashed value with the hash (using the same hashing function again) of the user entered password. Using a unique salt per user password as an extra security measure is also strongly advised, as this protects against what is known as rainbow table attacks.
For a more thorough explanation of what the preferred hashing algorithm to use is, and why, see this answer by user Andrew Moore to this question.
$abfrage = "SELECT username, password FROM logins WHERE username='$username' LIMIT 1";
$ergebnis = mysql_query($abfrage);
if(mysql_num_rows($ergebnis) > 0) // you can check for one
{
$row = mysql_fetch_object($ergebnis);
//rest code goes inside here
// redirect to any page or do whatever you like
}
I assume you're doing the login that way to protect against SQL injections, however, mysql_real_escape_string() will quickly fix that problem. I would do something like this:
$login = mysql_query("SELECT id FROM users WHERE username='" . mysql_real_escape_string($username) . "' AND password='" . mysql_real_escape_string($password) . "' LIMIT 1");
if ( mysql_num_rows($login) > 0 ) {
$user = mysql_fetch_array($login);
$_SESSION['userID'] = $user['id'];
echo 'Login successful!';
} else { // login failed
echo 'Login failed.';
}
Notice that I'm storing the user's ID in the session instead of their username. This is usually a better idea because it's much easier to manage user activity based on a unique auto_increment index value rather than a textual username. Most sites don't, but this method would allow you to let multiple users have the same username if you wished to do so. But if you do this, make sure you're logging them in based on their email address rather than their username, because you don't want someone getting logged in as someone else because they share the same username.
Also, you should be encrypting passwords. MD5 is a simple solution and can be implemented like so:
$password = md5($_POST['password']);
If you md5 the password input, you don't need to worry about slashes, either. Hope that helps.
Maybe the password is not gotten properly from the database. use print_r($row) to make sure everything is good there.
Also use sha1 encription with salt to increase security. Also do:
$username = mysql_real_escape_string($_POST["username"]);
$password = mysql_real_escape_string($_POST["password"]);
to avoid injection.
Hope this helps.
Okay, so I'm learning php, html, and mysql to learn website development (for fun). One thing I still don't get is how to use md5 or sha1 hashes. I know how to hash the plain text, but say I want to make a login page. Since the password is hashed and can't be reversed, how would mysql know that the user-inserted password matches the hashed password in the database? Here is what I mean:
$password = md5($_POST['password']);
$query = ("INSERT INTO `users`.`data` (`password`) VALUES ('$password')");
I know that this snippet of script hashes the password, but how would I use this piece of code and make a login page? Any working examples would be great.
Here is my script:
<?php
session_start();
include("mainmenu.php");
$usrname = mysql_real_escape_string($_POST['usrname']);
$password = md5($_POST['password']);
$con = mysql_connect("localhost", "root", "g00dfor#boy");
if (!$con) {
die(mysql_error()); }
mysql_select_db("users", $con) or die(mysql_error());
$login = "SELECT * FROM `data` WHERE (`usrname` = '$usrname' AND `password` = '$password')";
$result = mysql_query($login);
if (mysql_num_rows($result) == 1) {
$_SESSION['logged_in'] = true;
header('Location: indexlogin.php');
exit;
}
else {
echo "Wrong username or password.";
}
?>
But I still get the else statement, "Wrong username or password. Someone help plz!"
The answer is quite simple: You have a hash in the database, so you need to hash the user-provided password to compare them.
So when the user attempts to log in, you take the $_POST['password'] or whatever, and create a hash of it. Then, you simply query the database for the hash, SELECT * FROM users WHERE password = 'hashgoeshere'
I would also recommend you read more about secure storage of passwords. For example this is a good start: You're probably storing passwords incorrectly - Coding Horror
Please use SHA1/256. MD5 is not cryptographically secure anymore and it's discouraged to use it for cryptography (it's fine for file hashes ETC).
I'm not posting code, but explaining the technique:
First, on the registration, take the SHA1/256 hash of the password and store it in the database. The next time the user logs in you take the SHA1/256 hash of the password he/she entered again and match it against the hash stored in your database. This works because the SHA1 hash for the password is semi-unique (the chances for duplicates are small) for that password.
well instead of inserting into the SQL database, assign some query into a variable and check it against the md5 given by the user
When the user tries to login using their password, you take the md5 of what they enter and compare it with what you've already stored in the database. If it matches, you know they entered the right password.
It hashes the password so it is not save in clear text
e.g mylongpassword becomes 9a995d3f6a3d69c1a9b4344bed4f2c87
Select the hashed password using the db First
$password_from_db = Select * from user where username='".$_POST['username']."'
Then password from the ($_POST['password']) should be hashed first in PHP then compared to the valued stored in the DB
<?
if (md5($_POST['password'])==$password_from_db){
return true;
}else{
return false;
}
?>
Edited
Your code seems okay.
Check if your password field in the database is at least 32 characters.
And try to execute this query (changing variables to real string) in phpMyAdmin if you use one.
You have a major crypto problem, too.
$password = md5($_POST['password']);
The problem there is that all the people with identical passwords will have identical hashes, so if (when?) someone breaks into your site, they run one dictionary attack, and then compare the hashes from the attack to the hashes from your DB. As a result, they break every single account on your site for essentially the same cost as breaking one.
At a very minimum, you should salt it, something like this:
$password = md5($_POST['user'] + $_POST['password']);
But even that has surprising weaknesses, so it's better to hash it twice, maybe like this:
$password = md5($_POST['password'] + md5($_POST['password'] + $_POST['user']));
Of course, the best way of all is to use something written by someone who knows far more about doing crypto properly than I do :)
(And remember that bad perf in your authentication system is a feature.)
I need the following authentication script finished. I am weak at php/pdo so I do not know how to ask for the number of rows equalling one and then setting the session id's from the results of the query. I need to not only set the $_SESSION['userid'] but also the ['company'] and the ['security_id'] as well from the results.
here is what I have:
$userid = $_POST['userid'];
$password = $_POST['pass'];
if ( $userid != "" || $password != "" )
{
$sql = "SELECT * FROM contractors WHERE userid = '" . $userid . "' AND password = '" . $password . "'";
$result = $dbh->query( $sql );
} else
{
echo "login failed. Your fingers are too big";
}
Optional Information:
Browser: Firefox
DO NOT EVER USE THAT CODE!
You have a very serious SQL injection open there. Every user input that you take, whether from cookies or CGI, or wherever, must be sanitized before it's used in an SQL statement. I could easily break into that system by attempting a login with an username like:
user'; UPDATE contractors SET password = '1337'
... after which I could then login as anyone. Sorry if I sound aggressive, but what that code does is like forgetting to lock the front door into your company which probably doesn't even contain an alarm system.
Note that it doesn't matter whether the input is actually coming from the user or not (perhaps it's in a pre-filled, hidden from). From the security point of view, anything that comes from anywhere outside has to be considered to contain malicious input by the user.
As far as I know, you need to use the quote function of PDO to properly sanitize the string. (In mysql, this would be done with mysql_real_escape_string().) I'm not an expert on PDO, mind you, somebody please correct if I'm wrong here.
Also you probably shouldn't store any passwords directly in the database, but rather use a hash function to create a masked password, then also create a hash from the user provided password, and match the hashes. You can use the PHP hash function to do this.
As for other issues, I don't know if the approach you have on SQL SELECT is the best approach. I would just select the corresponding user's password and try matching that in the program. I don't think there's any fault in the method you're using either, but it just doesn't seem as logical, and thus there's a greater chance of me missing some bug - which in case of passwords and logins would create a window for exploits.
To do it your way, you need to notice that the result you are getting from the PDO query is a PDOStatement, that doesn't seem to have a reliable function to diretly count the amount of result rows. What you need to use is fetchAll which returns an array of the rows, and count that. However, as I said this all feels to me like it's open for failures, so I'd feel safer checking the password in the code. There's just too much distance from the actual password matching compasion for my taste, in such a security-critical place.
So, to the get the resulting password for the userid, you can use PDOStatement's fetch() which returns the contents of the column from the result. Use for example PDO::FETCH_ASSOC to get them in an associative array based on the column names.
Here's how to fix it:
$userid_dirty = $_POST['userid'];
$password_dirty = $_POST['pass'];
$success = false; // This is to make it more clear what the result is at the end
if ($userid != "" || $password != "") {
$userid = $dbh->quote($userid_dirty);
$passwordhash = hash('sha256',$password_dirty);
$sql = "SELECT userid, passwordhash, company, security_id FROM contractors WHERE userid = ".$userid;
$result = $dbh->query( $sql );
if ($result) { // Check if result not empty, that userid exists
$result_array = $result->fetch(PDO::FETCH_ASSOC);
if ($result_array['PASSWORDHASH'] == $passwordhash) {
// login success
$success = true;
// do all the login stuff here...
// such as saving $result_array['USERID'], $result_array['COMPANY'], $result_array['SECURITY_ID'] etc.
} // else fail, wrong password
} // else fail, no such user
} else {
// fail, userid or password missing
echo ' please enter user id and password.';
}
if (!$success) {
echo ' login failed.';
}
Of course, the code can be cleaned up a bit, but that should explain what needs to be done. Note that since the password is both hashed, and never used in the SQL, it doesn't actually need cleaning. But I left it there just in case, since in the original code it was used in the query.
Note that all the code concerning storing passwords need to be changed to store the hash instead of the password. Also, it would be a very good idea to use a salt added to the password before hashing.
Also, I provided the code simply for educational purposes - I just thought that code was the clearest way to explain how to do this. So do not mistake this site as a service to request code. :)
The php manual is an excellent resource for learning PHP. It looks like you know a little SQL, and you have heard of PDO, which is a good start. If you search google for "PDO", or look in the PHP manual for the term, you'll find the PDO section of the manual. It looks like you've found the ->query function, so now you need to see what that returns. Going to the that function's manual page, we see that it returns a PDOStatement object. The word PDOStatement is helpfully linked to the relevant page in the manual, which lists the methods available on that object. There is a rowCount() method that will likely do what you want.