I am trying to re-learn my web development skills for a current application project I am working on. I have verified my PHP back-end is getting the data passed to it from the HTML/JS form on the front-end. But when I submit the form the webpage does not mention any errors. Everything looks fine until I look at the database and see nothing was added. I do not see the echo at the end either that data was successfully added, or that it failed to add data.
register.php
<?php
include "conn.php";
if(empty($_POST['firstName']) || empty($_POST['lastName']) || empty($_POST['email']) || empty($_POST['password']) || empty($_POST['pin'])) {
echo "<br>Error: Please fill out all required fields.";
} else if($_POST['password'] != $_POST['confirmPassword']) {
echo "<br>Error: Passwords do not match.";
} else if($_POST['pin'] != $_POST['confirmPin']) {
echo "<br>Error: Pin codes do not match.";
} else if(strlen($_POST['password']) > 20 || strlen($_POST['password']) < 8) {
echo "<br>Error: Passwords must be between 8 and 20 characters in length.";
} else if(strlen($_POST['pin']) != 4) {
echo "<br>Error: Pin codes must be a maximum of 4 characters.";
} else if(strlen($_POST['firstName']) > 50 && strlen($_POST['lastName']) > 50 && strlen($_POST['email']) > 50) {
echo "<br>Error: First Name, Last Name, and E-mail fields must be shorter than 50 characters in length.";
} else if(!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
echo "<br>Error: You must use a valid e-mail address.";
} else {
echo "<br><br>Successfully checked all data!";
$options = [
'cost' => 12,
];
$hash = password_hash($_POST['password'], PASSWORD_BCRYPT, $options);
$numericPin = (int)$_POST['pin'];
$currentDateTime = date("Y-m-d H:i:s");
$stmt = $conn->prepare("INSERT INTO account (firstName, lastName, email, password, pin, registerDate) VALUES (:firstName, :lastName, :email, :password, :pin, :registerDate)");
$stmt->bindParam(":firstName", $_POST['firstName']);
$stmt->bindParam(":lastName", $_POST['lastName']);
$stmt->bindParam(":email", $_POST['email']);
$stmt->bindParam(":password", $hash);
$stmt->bindParam(":pin", $numericPin);
$stmt->bindParam(":registerdate", $currentDateTime);
if($stmt->execute()) {
echo "<br><br>Data added successfully";
} else {
echo "<br><br>Failed to add data";
}
}
?>
MySQL DB Structure:
MySQL DB Structure Picture Link
You have a misspelled placeholder
":registerdate"
You have this in the SQL
$stmt = $conn->prepare("INSERT INTO account (firstName, lastName, email, password, pin, registerDate) VALUES (:firstName, :lastName, :email, :password, :pin, :registerDate)");
Specifically :registerDate vs :registerdate, casing matters here.
A few other things:
change include to require ask youself will the script work without it, well no. Then it needs to be require. Include won't give you an error when it fails to find the file.
Change your validation to help your end users
$errors = [];
if(empty($_POST['firstName']) || empty($_POST['lastName']) || empty($_POST['email']) || empty($_POST['password']) || empty($_POST['pin']))
$errors[] ="<br>Error: Please fill out all required fields.";
if($_POST['password'] != $_POST['confirmPassword'])
$errors[] ="<br>Error: Passwords do not match.";
if($_POST['pin'] != $_POST['confirmPin']) ....
Then count the array
if(count($errors) > 0 ){
echo implode("\n", $errors);
}else{
//--- do insert
}
This way you can show them all the errors at once. Otherwise they have to submit, correct, submit, correct etc etc... This will get very frustrating very quickly and the last thing you want to do is frustrate your users, they may just leave. I have and will leave sites that don't show me the errors right away, I would rather find a site offering what I want that is properly build then deal with one that can't even do proper validation. It makes me wonder what else cant be trusted on the site. (but that is me and I have experience in this thing).
Naming Conventions (opinion):
I always name DB fields all lowercase with spaces replaced with _. This reduces a lot of these kinds of issues because, is it registerdate, registerDate or RegisterDate.
There are a few other things related to table names that I do, but I won't bore you with all that.
Cheers!
So just to add the answer here, it was simple. I added a try/catch block around where the statement was being executed (and where the script was dying). Found that there was some kind of duplicate placeholder usage or mismatch. Then I double checked my placeholders and found :registerDate was bound as :registerdate. Fixed and boom, adding to database. Thanks all for helping!
Related
I would like to be able to run a php validate script to stop users form entering gibberish as their email address. I know we can have the form input type as email but that can be easily bypassed in developer tools and the database integrity damaged.
My insert page looks like this:
$email = $conn->real_escape_string($_POST['emailpost']);
$password = $conn->real_escape_string($_POST['passpost']);
$firstname = $conn->real_escape_string($_POST['firstnamepost']);
$lastname = $conn->real_escape_string($_POST['lastnamepost']);
$phonenumber = $conn->real_escape_string($_POST['phonenumberpost']);
$education = $conn->real_escape_string($_POST['institutionpost']);
$facebook = $conn->real_escape_string($_POST['facebookpost']);
$twitter = $conn->real_escape_string($_POST['twitterpost']);
$instagram = $conn->real_escape_string($_POST['instagrampost']);
$filename = $_FILES['uploadprofileimg']['name'];
$filename = $ran.$filename;
$filetmp = $_FILES['uploadprofileimg']['tmp_name'];
$filetype = $_FILES['uploadprofileimg']['type'];
move_uploaded_file($filetmp, "../userimages/".$filename);
$insertuser = "INSERT INTO elmtree_users (user, email, pw, firstName, lastName, profileimg, learninginstitute, phone, facebook, twitter, instagram) VALUES
('$username', '$email', '$password', '$firstname', '$lastname', '$filename', '$education', '$phonenumber', '$facebook', '$twitter', '$instagram')";
$resultinsert = $conn -> query($insertuser);
if(!$resultinsert){
echo $conn->error;
}else{
echo "<h2> Account successfully registered!</h2>
<h4>Please <a href='login.php'> <font class='text-success'><strong>login.</strong></font></a></h4><br><br><br><br>";
Like everyone is pointing out
making your own logging system is tricky. it required you to do additional steps to make the content secured. Not only to hackers but you as administrator of the database shouldn't have access to see your customers password in PlainText Most users will use the same password on your site as they used for there email password they registered with on your site..
It is more advisable to create login tools like laravel, Or simply research how to build a secure login system, because what we are seeing here in your code, is BAD, Not syntactically, but from a security stand point.
Me knowing you store passwords like that, I wouldn't register onto your website.
Any how not only that, But you really should have a look into mysqli binding
Or even, and something I like better is PDO_Mysql
Your code will not only be more clear to read, but will bind values directly to a a field within mysql ( no need to use real_escape_string no more )
Now to actually answer your question.
You probably should make some kind of javascript live validator on the field of your form directly.
then on PHP side, You can do a simple condition with REGXP and preg_match()
Have a look at https://regex101.com/r/SOgUIV/1 this is a regex that will validate EMAILs.
With this link, You should then experiment a bit with it, it has not only documentation on the side but also possibles quantifier and such.
if(preg_match("/^((?!\.)[\w-_.]*[^.])(#\w+)(\.\w+(\.\w+)?[^.\W])$/i",trim($_POST['Email']))){
//What ever is in here will get process when $_POST['emailpost'] is valid.
}
Edited ----
As some user pointed out in comments.
You would probably be better of using
if(filter_var($_POST['emailpost'],FILTER_VALIDATE_EMAIL){
//What ever is in here will get process when $_POST['emailpost'] is valid
}
Also if you want to make sure user has access to the email address account, You could also add two column within your users table, isConfirmed,ConfirmationCode
When the user register, You create a unique code and put it into ConfirmationCode then send the user an email with something along those line "Please click the following link to activate account www.yourWebSite.com/confirmationPage.php?Code=$TheActualCodeYouCreatedForThatUser"
Then once user get to that page, Change the field isConfirmed to '1' or true.
Once there on your website, you will be able to assume that only emails with isConfirmed is a real user.
To validate email you need to check a lot of stuff like
if the email already exists
if its a real email i.e check for presence of #
check for funny characters which are not supposed to be in an email.
then always encrypt your password
if ($_POST['submit']) {
$errors = array();
$email = $conn->real_escape_string($_POST['emailpost']);
$password = $conn->real_escape_string($_POST['passpost']);
$firstname = $conn->real_escape_string($_POST['firstnamepost']);
$lastname = $conn->real_escape_string($_POST['lastnamepost']);
$phonenumber = $conn->real_escape_string($_POST['phonenumberpost']);
$education = $conn->real_escape_string($_POST['institutionpost']);
$facebook = $conn->real_escape_string($_POST['facebookpost']);
$twitter = $conn->real_escape_string($_POST['twitterpost']);
$instagram = $conn->real_escape_string($_POST['instagrampost']);
$filename = $_FILES['uploadprofileimg']['name'];
$filename = $ran.$filename;
$filetmp = $_FILES['uploadprofileimg']['tmp_name'];
$filetype = $_FILES['uploadprofileimg']['type'];
move_uploaded_file($filetmp, "../userimages/".$filename);
if (strlen($email) && strlen($password) && strlen($firstname) && strlen($lastname) && strlen($phonenumber) && strlen($education) && strlen($facebook) && strlen($twitter) && strlen($instagram)) {
//check for a valid email
if(preg_match("^[_a-z0-9-]+(\.[_a-z0-9-]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$^",$email))
$errors['email'] = 'invalid email address';
//check for presence of # in email
if (!stristr($em,"#") OR !stristr($em,".") $errors['email'] = 'please enter an email';
//echeck if email already exists in database
$checkemail = $conn->get_row("SELECT * FROM elmtree_users WHERE email=".$email);
if( $conn->num_rows( $checkemail ) > 0 ) $errors['email'] = "User already exists with the email: " . $email;
//validate password
$minpasslen = 8;
if (strlen($password) < $minpasslen)
$errors['email'] = 'password is too short';
$finalpassword = MD5($password);
if (empty($errors)) {
$insertuser = "INSERT INTO elmtree_users (user, email, pw, firstName, lastName, profileimg, learninginstitute, phone, facebook, twitter, instagram) VALUES
('$username', '$email', '$finalpassword', '$firstname', '$lastname', '$filename', '$education', '$phonenumber', '$facebook', '$twitter', '$instagram')";
$resultinsert = $conn -> query($insertuser);
if(!$resultinsert){
echo $conn->error;
} else {
echo "<h2> Account successfully registered!</h2>
<h4>Please <a href='login.php'> <font class='text-success'><strong>login.</strong></font></a></h4><br><br><br><br>";
} else {
echo implode('<br>', $errors);
}
}
}
I want to check the username to contain only letters. If it doesn't contain any letters, the registration I want to fail. Is it good to use preg_match? Now if I input for username: a1, also filling the password, confirm password and type fields the registration is successful.
form.php
if (isset($_POST['submit'])){
/* if the username is alphabetic */
if (ctype_alpha(str_replace(' ', '', $username)) === false &&!empty($username) ) {
$errors[] = 'Userame must contain letters and spaces only';
}
/* if passwords match */
if((!empty($password) && !empty($repassword)) && ($password != $repassword)){
echo "Passwords do not match.";
}
if (!empty($_POST['username']) && !empty($_POST['password']) && !empty($_POST['type'])){
$sql = "INSERT INTO users (username, password, type) VALUES ('$username', '$password', '$type')";
$result = mysql_query($sql);
echo 'Successful Registration.';
}
else{
echo 'Please fill all the fields.';
}
}
If you're looking for just letters in the username, preg_match will work for you.
For example,
if(!preg_match("/^[a-zA-Z]+$/", $username)){
// if the username has non-letter characters
}
However, like the comments are saying, there are much better ways to approach preventing SQL injection attacks. Switching to prepared statements is a great start.
I am very new to PHP and Mysql. I have made a registeration form but the values being inputted are not being saved in my database. I don't know why. I am connected to the database. Could anyone give me some insight? By the way, I know you are going to say "Mysql" is deprecated. But I am just starting out and learning how all of this works. As soon as I have a thorough understanding of the processes I am going to change my code to Mysqli...
<?php
//form data
$submit = strip_tags($_POST['submit']);
$fname = strip_tags($_POST['fname']);
$lname = strip_tags($_POST['lname']);
$usernamereg = strip_tags($_POST['usernamereg']);
$passwordreg = strip_tags($_POST['passwordreg']);
$email = strip_tags($_POST['email']);
$emailcheck = strip_tags($_POST['emailcheck']);
$date = date("Y-m-d");
if($submit)
{
//check for existence
if($fname&&$lname&&$usernamereg&&$passwordreg&&$email&&$emailcheck)
{
//encrypt password
$password = md5($passwordreg);
if(strlen($usernamereg)>25)
{
echo "Username must be 25 characters or less.";
}
else
{
//checks password length
if(strlen($passwordreg)<6)
{
echo "Passwords must be atleast 6 characters long";
}
else
{
if($email!=$emailcheck)
{
echo "emails to not match";
}
else
{
//open database
$connect = mysql_connect("localhost","root","clandestine");
mysql_select_db("user_db"); //selects database
$queryreg = mysql_query("INSERT INTO users VALUES('','$date','$fname','$lname','$usernamereg','$passwordreg','$email','$emailcheck'");
echo "You have been registered!";
}
}
}
}
else
echo "Please fill in <b>all</b> fields!";
Try assigning the columns in the INSERT query.
$queryreg = mysql_query("INSERT INTO users (`randomField`, `date`, `first_name`, `last_name`, `username`, `password`, `email`, `email_check`) VALUES ('','$date','$fname','$lname','$usernamereg','$passwordreg','$email','$emailcheck'");
What is the first column supposed to be?
Have you done any sanity checking? (ie, printing test data to the screen at certain points in the code to make sure your IF statements are evaluating to true?
Additionally, try saving your INSERT query as a variable string:
$query = "INSERT INTO.............";
and then printing it to the screen. Copy and paste that query into PHPMyAdmin (if you have access to it) and see if there are any errors with your statement. PMA will tell you what errors there are, if any.
EDIT: Also, please don't ever MD5 a password or other highly sensitive data. Use a secure algorithm and salt the password. If you're unsure of what this all means:
refer to this link
What do you get if you do:
$query = "INSERT INTO users
(date, first_name, last_name, username, password, email, email_check)
VALUES
('$date','$fname','$lname','$usernamereg','$passwordreg','$email','$emailcheck')";
mysql_query($query)or die('Error: <br />'.$query.'<br />'.mysql_error());
Note the removal of the backticks was just to simplify the code. It's correct to leave them in but with no spaces etc in your column names it should work anyway. Oh, and this is NOT good practice for production, of course. Just really clear debug.
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
}
}
}
I am setting up a new PHP app and would like to learn to salt and secure user password. I am unsure about which step during registration I need to do this at. Also, do I need to change my login forms as well?
if(isset($_POST['submit'])){
//protect and then add the posted data to variables
$username = protect($_POST['username']);
$password = protect($_POST['password']);
$passconf = protect($_POST['passconf']);
$email = protect($_POST['email']);
//check to see if any of the boxes were not filled in
if(!$username || !$password || !$passconf || !$email){
//if any weren't display the error message
echo "<center>You need to fill in all of the required filds!</center>";
}else{
//if all were filled in continue checking
//Check if the wanted username is more than 32 or less than 3 charcters long
if(strlen($username) > 32 || strlen($username) < 3){
//if it is display error message
echo "<center>Your <b>Username</b> must be between 3 and 32 characters long!</center>";
}else{
//if not continue checking
//select all the rows from out users table where the posted username matches the username stored
$res = mysql_query("SELECT * FROM `users` WHERE `username` = '".$username."'");
$num = mysql_num_rows($res);
//check if theres a match
if($num == 1){
//if yes the username is taken so display error message
echo "<center>The <b>Username</b> you have chosen is already taken!</center>";
}else{
//otherwise continue checking
//check if the password is less than 5 or more than 32 characters long
if(strlen($password) < 5 || strlen($password) > 32){
//if it is display error message
echo "<center>Your <b>Password</b> must be between 5 and 32 characters long!</center>";
}else{
//else continue checking
//check if the password and confirm password match
if($password != $passconf){
//if not display error message
echo "<center>The <b>Password</b> you supplied did not math the confirmation password!</center>";
}else{
//otherwise continue checking
//Set the format we want to check out email address against
$checkemail = "/^[a-z0-9]+([_\\.-][a-z0-9]+)*#([a-z0-9]+([\.-][a-z0-9]+)*)+\\.[a-z]{2,}$/i";
//check if the formats match
if(!preg_match($checkemail, $email)){
//if not display error message
echo "<center>The <b>E-mail</b> is not valid, must be name#server.tld!</center>";
}else{
//if they do, continue checking
//select all rows from our users table where the emails match
$res1 = mysql_query("SELECT * FROM `users` WHERE `email` = '".$email."'");
$num1 = mysql_num_rows($res1);
//if the number of matchs is 1
if($num1 == 1){
//the email address supplied is taken so display error message
echo "<center>The <b>E-mail</b> address you supplied is already taken</center>";
}else{
//finally, otherwise register there account
//time of register (unix)
$registerTime = date('U');
//make a code for our activation key
$code = md5($username).$registerTime;
//insert the row into the database
$res2 = mysql_query("INSERT INTO `users` (`username`, `password`, `email`, `rtime`) VALUES('".$username."','".$password."','".$email."','".$registerTime."')");
//send the email with an email containing the activation link to the supplied email address
You absolutely must read this article: Enough with the rainbow tables.
Summary: If you're not using BCrypt, you're doing it wrong. No ifs, no buts. (This also means that all the suggestions to use MD5 or SHA-1 or SHA-512 or anything else are wrong too.)
As for when you do it, it should be sometime before you insert it into the DB but after you check it for errors.
Some suggestions though.
Instead of nesting the ifs during error checking so that if username fails, password doesn't get checked, and if password fails, passconf doesn't get checked try something like this:
$errors = array();
if(strlen($username) > 32 || strlen($username) < 3)
{
$errors['username'] = "Username must be between 3 and 32 characters.";
}
else
{
$res = mysql_query("SELECT * FROM `users` WHERE `username` = '".$username."'");
$num = mysql_num_rows($res);
if($num == 1)
{
$errors['username'] = "Username already exists!";
}
}
if(strlen($password) < 5 || strlen($password) > 32)
{
$errors['password'] = "Password must be between 5 and 32 characters.";
}
else if($password != $confpass)
{
$errors['password'] = "Passwords do not match.";
}
etc. etc. etc. so that each field is checked and errors returned if there are any. Then you do something like this at the end:
if(!count($errors)) //or if(count($errors) == 0)
{
//code to process login/registration/whatever Do password hashing here.
}
else
{
//There were errors, do something else
}
This way you get all errors, so you can tell the user everything that's wrong with their input at once, and the code isn't as deeply nested.
Also, the people having the flame war on what hashing algorithm to use above, just ignore them unless you're trying to create a US Government or Corporate application. No attackers will care enough to actually attack otherwise, unless your application gets popular enough to warrant an attack. It is important that you hash it in some way though.
SECURITY IS HARD. Don't do it yourself but let the exports figure it out. You could read there specs/implementations(if open):
openid
google friend connect
facebook connect
twitter single sign in
just to name a few options.