This question already has answers here:
How to check if a row exists in MySQL? (i.e. check if username or email exists in MySQL)
(4 answers)
Closed 2 years ago.
I am creating a very simple registration form in php, currently when the user tries to register there will popup a javascript alert with a succes or fail message.
Now I want to catch the sql exception to show if the username or email already excists in the database instead of a standard fail message.
This is the code I have so far:
if(isset($_POST['btn-signup']))
{
$uname = mysql_real_escape_string($_POST['uname']);
$email = mysql_real_escape_string($_POST['email']);
$upass = md5(mysql_real_escape_string($_POST['pass']));
if(mysql_query("INSERT INTO user(username,password,email) VALUES('$uname','$upass','$email')"))
{
?>
<script>alert('successfully registered ');</script>
<?php
}
else{
?>
<script>alert('error while registering you...');</script>
<?php
}
}
?>
How can I check if the email or username already excists in the database? Both variable's are already unique in the database.
From Comments:
I don't want 2 queries while the database can return an exception for me. If there are about 10 million records in that table, I don't want to check them all before inserting a new one.
Ok, so you have one query to insert and check is unique? So you have to INSERT on a UNIQUE_INDEX MySQL column, you can catch these sort of exceptions with the following style of answer shameless stolen from this answer to this question:
In the case of this answer we'll assume you're using PDO, because you should. Please read up about it.
// Pre-setup the database connection somewhere, usually an include (or a class)
$link = new PDO("mysql:host=$dbhost;dbname=$dbname",$dbusername,$dbpassword);
// PDO needs to be set in Exception mode:
$link->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
//Input Processing functions.
// (entirely optional)
$uname = MyCleanerFunction($_POST['uname']);
$email = MyCleanerFunction($_POST['email']);
//please see note below re:MD5
//$upass = md5($_POST['pass']);
$options['cost'] = 12;
$upass = password_hash($_POST['pass'],PASSWORD_BCRYPT,$options);
//now reach the code part:
try {
//PDO query execution goes here:
$statement = $link->prepare("INSERT INTO user(username,password,email) VALUES(:uname, :email, :pass)"));
$statement->bindValue(":uname", $uname);
$statement->bindValue(":email", $email);
$statement->bindValue(":pass", $upass);
$statement->execute();
//reaching here everything is ok!
}
catch (\PDOException $e) {
if ($e->errorInfo[1] == 1062) {
// The INSERT query failed due to a key constraint violation.
// THIS means that it failed because the Primary Key
// (the email) appears already in the database table.
}
if($e->errorInfo[1] == 9999){
// possible other IF clauses for other errors in INSERT.
}
}
You would also do well to read up about catching and outputting PDO errors. As well as all about MySQL Unique Key Constraints.
Also very useful alternative viewpoint that you Should not catch PDO exceptions.
Also please note that MD5 is an extremely weak hash for storing passwords and that PHP password_hash function is the much preferred way of doing it.
PLEASE use Prepared Statements for your MySQL interactions, the layout above is a rough guide to how they look and is very similar for MySQLi and PDO. Prepared Statements go a long way towards securing your data from malicious user input.
$con=mysqli_connect("localhost","root","","my_db");
$check="SELECT COUNT(*) FROM persons WHERE Email = '$_POST[eMailTxt]'";
if (mysqli_query($con,$check)>=1)
{
echo "User Already in Exists<br/>";
}
else
{
$newUser="INSERT INTO persons(Email,FirstName,LastName,PassWord) values('$_POST[eMailTxt]','$_POST[NameTxt]','$_POST[LnameTxt]','$_POST[passWordTxt]')";
if (mysqli_query($con,$newUser))
{
echo "You are now registered<br/>";
}
else
{
echo "Error adding user in database<br/>";
}
}
Related
This question already has answers here:
How can I prevent SQL injection in PHP?
(27 answers)
Can I mix MySQL APIs in PHP?
(4 answers)
Closed 3 years ago.
I am very new to mysqli earlier i am writing queries in mysql but mysqli is more advanced so, i am first time using it.
Below is my php code.
function clean($str) {
$str = #trim($str);
if(get_magic_quotes_gpc()) {
$str = stripslashes($str);
}
return mysql_real_escape_string($str);
}
$email = clean($_POST['email']);
$password = clean($_POST['password']);
//$password =md5($password);
if(empty($res['errors'])) {
$result = $mysqli->query("SELECT uid FROM users where email='$email' and password = '$password'");
if($result->num_rows == 1){
$res['success'] = true;
}
else{
array_push($res['errors'], 'Invalid login details');
$res['success'] = false;
}
}else{
$res['success'] = false;
}
echo json_encode($res);
}
clean function is not working as expected because sql queries return false if i enter username and password correct.
So, it seems like this is not valid in mysqli case.
I checked this link PHP MySQLI Prevent SQL Injection and got to know that we have to prepare query.
I can see there is an example but i am not able to understand how to prepare/bind if i have to use two or more form data.
Thanks for your time.
Updated code
$result = $mysqli->prepare("SELECT uid FROM users where email=:email and password = :password");
$result->execute([
':email' => $email,
':password' => $password]);
//$result->execute();
if($result->num_rows == 1){
//if(mysqli_num_rows($result) === 1) {
$res['success'] = true;
}
else{
array_push($res['errors'], 'Invalid login details');
$res['success'] = false;
}
As already stated in comments, you need to be consistent with your API choice. You can't mix APIs in PHP.
You started out with mysqli_*, so I'll continue with that. You had some mysql_* and PDO in there, and it might not be a bad idea to use PDO over mysqli_* - but if your server supports mysqli_*, there is nothing wrong with using that. See Choosing an API and decide for yourself (just stay away from mysql_*, it's outdated).
Using mysqli_*, you connect to the database like this (you didn't show your connection).
$mysqli = new mysqli("host", "username", "password", "database");
if ($mysqli->connect_errno) {
echo "Failed to connect to MySQL: (".$mysqli->connect_errno.") ".$mysqli->connect_error;
}
$mysqli->set_charset("utf8");
As for preventing SQL injection in it self, all you need is to use prepared statements. You can still clean or sanitize your data if there are some kind of values you don't want sitting in your tables - but that's kind of another discussion.
You also need to know if your passwords are hashed in the database. They really should be, and you should be using password_hash($password, $algorithm) and password_verify($password, $hash) if you're on PHP5.5 and above (if not, look into something like password_compat).
You need to be consistent with your hashes too, you can't insert it with md5 and selecting it with no hash. It all needs to be the same. Because if you are selecting an md5 hash, and comparing it to an unhashed string, they will be different, and the query fails.
I'm showing you an example of using password_verify(), so that means that the password stored in the database will also need to be stored with password_hash() (or your query fails).
if ($stmt = $mysqli->prepare("SELECT uid, password FROM users where email=?")) {
$stmt->bind_param("s", $_POST['email']); // Bind variable to the placeholder
$stmt->execute(); // Execute query
$stmt->bind_result($userID, $password); // Set the selected columns into the variables
$stmt->fetch(); // ...and fetch it
if ($stmt->num_rows) {
if (password_verify($_POST['password'], $password)) {
// Password was correct and matched the email!
} else {
// Password was incorrect...
}
} else {
// Accountname not found
}
}
This is just a basic example, but it will get you started. Never trust user input, use prepared statements.
You can bind more variables like so:
$stmt = $mysqli->prepare("SELECT uid FROM users where email= ? and password = ?");
$stmt->bind_param('ss', $email, $password);
/* execute prepared statement */
$stmt->execute();
As you can see, you can expand on the bind_param() function. You can also add different type of variables:
i corresponding variable has type integer
d corresponding variable has type double
s corresponding variable has type string
b corresponding variable is a blob and will be sent in packets
From: http://php.net/manual/en/mysqli-stmt.bind-param.php
First of all, I suggest you learn PDO instead of MySQLi, just because it supports more drivers.
Second, you use mysql_real_escape_string, as you might see, that is a MySQL function, not a MySQLi function.
So where you have:
$result = $mysqli->query("SELECT uid FROM users where email='$email' and password = '$password'");
You should do something like:
<?php
$stmt = $dbConnection->prepare("SELECT uid FROM users where email = :email AND password = :password");
try{
$stmt->execute([
':email' => $email,
':password' => $password
]);
}
catch(Exception $e){
echo $e->getMessage(); //Remove when putting online
}
if($stmt->num_rows){
$res['success'] = true;
}
?>
You're presently mixing MySQL APIs/functions with mysql_real_escape_string(), then num_rows and then a PDO binding method where email=:email and password = :password which seems to have been taken from another answer given for your question.
Those different functions do NOT intermix.
You must use the same one from connection to querying.
Consult: Can I mix MySQL APIs in PHP?
It looks like you're wanting to setup a login script. I suggest you use the following and pulled from one of ircmaxell's answers:
Pulled from https://stackoverflow.com/a/29778421/
Just use a library. Seriously. They exist for a reason.
PHP 5.5+: use password_hash()
PHP 5.3.7+: use password-compat (a compatibility pack for above)
All others: use phpass
Don't do it yourself. If you're creating your own salt, YOU'RE DOING IT WRONG. You should be using a library that handles that for you.
$dbh = new PDO(...);
$username = $_POST["username"];
$email = $_POST["email"];
$password = $_POST["password"];
$hash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $dbh->prepare("insert into users set username=?, email=?, password=?");
$stmt->execute([$username, $email, $hash]);
And on login:
$sql = "SELECT * FROM users WHERE username = ?";
$stmt = $dbh->prepare($sql);
$result = $stmt->execute([$_POST['username']]);
$users = $result->fetchAll();
if (isset($users[0]) {
if (password_verify($_POST['password'], $users[0]->password) {
// valid login
} else {
// invalid password
}
} else {
// invalid username
}
It's safer and uses a safe password hashing method, rather than what you seem to want to use is MD5 $password =md5($password); and is no longer considered safe to use now.
References:
PDO connection http://php.net/manual/en/pdo.connections.php
PDO error handling http://php.net/manual/en/pdo.error-handling.php
To check if a user exists, you can see one of my answers https://stackoverflow.com/a/22253579/1415724
http://php.net/manual/en/mysqli.error.php
http://php.net/manual/en/function.error-reporting.php
Sidenote: If you do go that route, remember to read the manuals and that your password column is long enough to hold the hash. Minimum length is 60, but they recommend 255.
It is also unclear if your HTML form does have name attributes for the POST arrays, so make sure the form is using a POST method.
http://php.net/manual/en/tutorial.forms.php
I believe I have given you enough information to get started.
What you must NOT do, is to use the above with your present code and simply patching it. You need to start over.
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.
The basic control structure I'm trying to get to work is to query the DB with the username and email, both of which are unique keys, and if either are in the DB let the user know that they have been taken and to please pick something else. The problem I'm running into is getting the result data in a usable form that I can then check the user-supplied data against.
I cut out the prepared statements for insertion from the snippit, as well as the validation routines, since both of them are working fine.
DB connection snippit
try {
if(!($dbc = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME))){ // Creates the $dbc variable object so we can
// have a connection to the database.
// uses mysqli functions.
throw new Exception;
}
}
catch (Exception $e) {
echo '<p>Could not connect to the database. Please contact the system administrator.</p>';
}
Snippit of Registration script
//before this was validation routines, if anything was wrong the script generated something into $reg_errors which is an array.
if(empty($reg_errors))
{
//queries database if there are any matches for username or email from user input.
if($stmt = $dbc->prepare("SELECT `email`, `username` FROM `users` WHERE `email` = ? OR `username` = ?"))
{
$stmt->bind_param("ss", $e, $u);
$stmt->execute();
$stmt->store_result();
$rows = $stmt->num_rows; //gives the number of rows returned from SELECT query. 0 means no dupes, 1 means one record has BOTH email and username, 2 means two different records (one with email, one with username)
##THIS IS WHERE I'M RUNNING INTO TROUBLE GETTING THE DATA IN A USABLE FORM##
$stmt->close();
} else {
echo "<p>Can't talk to database right now. Try again later, please.</p>";
}
if($rows==0) //no dupes of username or email, so let's try and add them into the DB
{
//prepared statement for insertion into DB
//also get's the count of affected rows. 1 means record inserted correctly.
//asks DB if a new row was created, and if so, thanks user for
//registration on the site & sends an email to their email.
//if query doesnt work, an error is triggered
if($count==1) {
//constructs a thank you note and emails it to the user, using the email they supplied.
exit();
} else {
echo "<p>Unable to process your registration at this time. Please try again later..</p>";
}
} else { // both username and email might be already used in DB, and error msgs are generated for array.
if($rows==2) { // this checks to make sure both entries are dupes
$reg_errors['email'] = 'This email address has already been registered. If you have forgotten your password, use the link to the right to have your password sent to you.';
$reg_errors['username'] = 'This username has already been registered. Please try another.';
} else { //this checks to see which of the two (email or username) is already in DB if both arent dupes.
if((__NEED SOMETHING HERE FROM DB QUERY___ == $_POST['email']) && (__NEED SOMETHING HERE FROM DB QUERY___ == $_POST['username'])) { //both match entries in DB
$reg_errors['email'] = 'This email address has already been registered. If you have forgotten your password, use the link to the right to have your password sent to you.';
$reg_errors['username'] = 'This username has already been registered with this email address. If you have forgotten your password, use the link to the right to have your password sent to you.';
} elseif(__NEED SOMETHING HERE FROM DB QUERY___==$_POST['email']) { // email match
$reg_errors['email'] = 'This email address has already been registered. If you have forgotten your password, use the link to the right to have your password sent to you.';
} elseif(__NEED SOMETHING HERE FROM DB QUERY___==$_POST['username']) { // username match
$reg_errors['username'] = 'This username has already been registered. Please try another one.';
}
} // end of $rows==2 ELSE
} // end of $rows == 0 IF
} else { // end of empty reg_errors conditional
//do something if the reg_error array isnt empty..
}
i'm pretty sure the answer lies in iterations and using meta_data from the result mysqli object, but after beating my head against a wall for a couple days and pouring over the mysqli php manual pages like a maniac, I'm still no closer to figuring out what I should be doing. Could anyone point me in the correct direction?
Starting from the registration script, have you tried this:
if($stmt = $dbc->prepare("SELECT `email`, `username` FROM `users` WHERE `email` = ? OR `username` = ?"))
{
$stmt->bind_param("ss", $e, $u);
$stmt->execute();
$stmt->bind_result($email, $username);
$rows = $stmt->num_rows;
//Move Conditionals Up a Little
if( $rows == 0 ) { //If No Records are Found
//Continue Registration
}
else if( $rows == 1 ) { //If One Record is Found
$stmt->fetch();
//Do Something With $email and $username from DB Here
}
else { //If More than One Record is Found
while( $stmt->fetch() ) { //Iterate Through Records
//Do Something With $email and $username from DB Here
}
}
}
This register form was made by me, but it doesn't do what I want it to do.
I want it to connect to a mysql database and store the information that was given by the form. I want it to hash the $password in md5 and store it in the "gebruikers" table. Please don't reply with "Damn, you have no idea what you are doing" or something like that. I am learning PHP by looking to examples and following tutorials. Please keep in mind that the mysql insert code is not filled in right, because I got stuck a few lines above.
So, my question is: I want to check if the mysql table already contains $email. If it IS already in the mysql table, I want to display an error message that I can place somewhere else in my PHP page. If the email adress given is unique, than the $password should hash into md5 and store into the mysql database, just like the other form entries.
How do I do that?
<?php
// Fetching all the form details
$email = $_POST["email"];
$password = $_POST["password"];
$voornaam = $_POST["voornaam"];
$tussenvoegsel = $_POST["tussenvoegsel"];
$achternaam = $_POST["achternaam"];
$dag = $_POST["dag"];
$maand = $_POST["maand"];
$jaar = $_POST["voornaam"];
$straat = $_POST["straat"];
$postcode = $_POST["postcode"];
$woonplaats = $_POST["woonplaats"];
$cniveau = $_POST["cniveau"];
$oniveau = $_POST["oniveau"];
$voornaam = $_POST["voornaam"];
$aboutme = $_POST["aboutme"];
//Here's where I don't know how to continue
$check = mysql_query("SELECT * FROM `gebruikers` WHERE `email` = '$email'");
if($check === FALSE) {
//there is a user already registered
echo("$email is al in gebruik. <a href='login.php'>Inloggen</a>?");
} else {
//There isn't a username
//mysql_query("INSERT INTO `user` (`id` ,`username` ,`password`) VALUES (NULL , '{$_POST['email']}', MD5( '{$_POST['password']}' ))");
echo("You have been registered!");
}
P.S.: I'm not a native English speaker, so please ignore my grammar mistakes/typos.
First of all, you made a major mistake: There is a SQL-Injection security hole.
Please read this: http://php.net/manual/en/security.database.sql-injection.php
Second, you should use mysqli instead of mysql, because mysql is deprecated.
Your error is that SQL does only return false if the query is invalid, not if there are no results. So the correct way of checking if there are results is to use http://php.net/manual/en/mysqli-result.num-rows.php
$result = mysql_query("SELECT * FROM `gebruikers` WHERE `email` = '$email' LIMIT 1");
if(mysql_fetch_array($result) !== false)
{
...
} else {
....
}
You should also read up on preventing SQL injection.
Maybe you've forgot to set the mysql_connect statement.
But I strongly recommend you stick from now on, with the mysqli_ functionality, since, as Aragon0 said, mysql is deprecated in PHP's newest versions.
Besides, mysqli statements are simpler than the mysql ones, for example you use one statement (mysqli_connect) to connect to your host and select your database at the same time, instead of using separated statements (both mysql_connect and mysql_select_db).
Oh, and no additional service package is required to use it. :)
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 (:
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Practises for getting information from $_GET/$_POST and saving it to a database?
Just wondering what exactly I should look out for with regards to safety in MySQL database insertions for users entering strings.
Currently all I'm doing is mysql_real_escape_string($string) for every $_GET or $_POST input I wish to put in the database. Is that cool? What else do I need to do?
$stmt = mysqli_prepare($con,"INSERT INTO friend_request (ToUID, FromUID) VALUES (?,?)");
mysqli_stmt_bind_param($stmt, "ii", $fid, $uid); //i-> Integer, S -> string
mysqli_stmt_execute($stmt);
if (mysqli_stmt_affected_rows($stmt))
echo 'Request Sent';
else
echo 'Something went wrong !';
This is my process:
//check to see if the request came from your server or not
$server = substr($_SERVER[HTTP_REFERER],0,LENGTH OF INCOMING PAGE);
$prevPage = ""; //the incoming page
if ($server != $prevPage) {
header("Location: $prevPage");
exit;
} else {
//pull and sanitize the data
include('link.php'); //include the link to your database
$var = $link->mysql_real_escape_string($_POST['data']);
//check for null values in required fields
//run the data through a regular expression check
//then store the information
There is so much more you could do to validate the data being entered.