The site I'm working on stores e-mail addresses in a hashed form by password_hash()
I know it's meant for passowrds, but the site is already up, so no going back.
Now when a new user tries to register, I need to check if the e-mail address that the user is using isn't already in the database. But since all e-mail addresses are hashed, I can't simply use:
$mail_query = mysql_query("SELECT email FROM userbase WHERE email='$email'");
$mail_num = mysql_num_rows($mail_query);
if ($mail_num != 0) {
$error = 'This email address is already in use.';
Is there any way to do it?
The site uses old MySQL api.
The only way, but very baaaad is to get AAAAALLLL emails and check them all... Sorry, don't even remember MySQL_* functions, but first you need to do such query:
SELECT email FROM userbase
and then, when you get all emails do loop and use password_verify on every single email... So in your loop you should have something like:
if(password_verify($email,$hash)) {
echo "Email is already in use!";
break;
}
that's it. Good luck. But I don't like such hashing: what if you need to remind users password?
Related
I have google this alot, but unfortunatilty found no working solution.
I know its a bad technique, but I need to send user its password by email.
I have manage to sent user Hash password, but i am unable to decrypt this password.
The following is the procedure i am using.
$results = DB::select("select * from dockl_users where email='" . Input::get('email') ."';");
foreach($results as $data){
$password= $data->password;
$email= $data->email;
}
Mail::send('passwordRecovery', array('email' =>$password), function($message)
{
$message->to(Input::get('email') )->subject('Password Recovery');
});
The above code send Encrypted password to the user by email but when i try to decrypt, it gives me following error message.
$decrypt= Crypt::decrypt($data->password);
Invalid data.
throw new DecryptException("Invalid data.");
Kindly guide me how to achieve this..
Short answer is that you don't 'decrypt' the password (because it's not encrypted - it's hashed).
The long answer is that you shouldn't send the user their password by email, or any other way. If the user has forgotten their password, you should send them a password reset email, and allow them to change their password on your website.
Laravel has most of this functionality built in (see the Laravel documentation - I'm not going to replicate it all here. Also available for versions 4.2 and 5.0 of Laravel).
For further reading, check out this 'blogoverflow' post: Why passwords should be hashed.
For compare hashed password with the plain text password string you can use the PHP password_verify
if(password_verify('1234567', $crypt_password_string)) {
// in case if "$crypt_password_string" actually hides "1234567"
}
EDIT: based on first reply I got below,I reworked my code and it now works... first checking the given email address to find the gamer id. Then checking the verfication state based on the gamer id. So if they change their email address in the future it will still know whether it's already been verified.
Below is my final code, (I've changed some name for items, so its not an exact copy/paste of my own code).
function email_not_verified ($email) { //check it's not already verified
include ('../connect.php'); // Include connect to database functions
$findUser= $db->prepare("SELECT game_id FROM players WHERE email=?");
$findUser->execute(array($email));
$user = $findUser->fetch();
if ( $findUser){
$veri= $db->prepare("SELECT sent_verification FROM players WHERE game_id=?");
$veri->execute(array($user["game_id"]));
$results = $veri->fetch();
$final = $results["sent_verification"];
}
if ($final == 1){
return TRUE;
}
else{
return FALSE;
}
}
Thanks again for the help.
Below, is my original question.
I'm trying to figure out a simple setup that stops a user repeatedly verifying their email address. As when they verify their email I'm awarding them a bonus of 300 credits for in store game purchases. I obviously don't want to keep dishing that out each time they follow their emailed verification link.
So I'm trying to run a check first, before the normal verification script is run.
But surprise, surprise: its not working...
I was trying to search my database for the email address with the verification field set to '1', I'd then see how many times it found this result. If it found it '0' times then that's fine to verify, if it found it once then its already been verified before.
function email_not_verified ($email) {
include ('../connect.php'); // connect to database
//check it's not already verified
$checkEmail= $db->prepare("SELECT * FROM players WHERE sent_verification=?, email=?");
$checkEmail->execute(array('1', $email));
$check2 = $checkEmail->rowCount();
if ($check2 = 1){
return TRUE;
}
else{
return FALSE;
}
}
I've been using
file_put_contents('results.txt',$check2);
to see the results of the code regardless of whether its putting out a TRUE or FALSE. But the result comes back as '0', even though I can see from looking at my database it should be '1'.
I'm not sure if there's a whole easier way to approach this, I keep trying to get my head around bind values but it's not yet sinking in... I'll continue to try.
Thanks for any help, guidance, pointing out the obvious... I feel like I've taken the wrong path with my script but can't think how else to approach it...
Cheers
Jon
Your if statement is wrong. You're using the assignment operator instead of comparison. This doesn't matter though because rowCount isn't always reliable, which is probably where the actual problem is. What you need to do is fetch the first row and see if you get a row back.
However, you probably don't want to attach this to e-mail verification. When users change their e-mail address, you will want to verify that new address and you probably don't want to give them 300 more credits each time they do. Otherwise, someone could programmatically change their e-mail address over and over again, creating a lot of credits for themselves.
I would separate out the 300 free credits as a coupon or something that can only be used once per account. On e-mail verification, if that coupon hasn't already been used up for that account, use it and mark it as such in your database. This could be done simply by adding another column for new_account_bonus_credits or something.
We have an existing membership site setup in php/MySQL, and are looking to integrate Facebook registration and login. The login and registration forms have been imported, and are hooked up to php/MySQL code which creates new users in the database. However, in order that we can prevent a single user having multiple logons (eg. one site logon, one Facebook logon), we need to check the email address of the user before adding a new line to the members database table.
We can do this through php/MySQL code, but that gets needlessly complex when you have to cater for all the permutation of cross-site/FB membership, plus we do not want the user to connect to the FB registration app without becoming a site member. A better solution would be to prevent the user from registering under an email address which is already present in the database (rather directing them to obtain a new password, and update connect their facebook profile to the existing profile). We have been looking for weeks for a solution, and have consulted the Facebook Developers Async Validation (http://developers.facebook.com/docs/plugins/registration/advanced/), as well as the stackexchange forum, but have not been able to implement a solution which validates the email address against the database, and prevents/allows registration accordingly.
The code we have so far is
<fb:registration redirect-uri="http://www.mysite.com/register.php"
fields='[{"name":"name"},{"name":"email"}
{"name":"username","description":"Username","type":"text"}]'
onvalidate="validate_async"></fb:registration>
<script src="https://code.jquery.com/jquery-1.4.4.min.js"></script>
<script>
function validate_async(form, cb) {
$.getJSON('http://www.mysite.com/register_check.php/' + form.email + '?callback=?',
function(response) {
if (response.error) {
// Username isn't taken, let the form submit
cb();
}
cb({username: 'That email is taken'});
});
}
</script>
register_check.php
//connect to the database
include 'mysql_connect.php'
$email = $_GET('email');
$data = array();
$sqlCommand = "SELECT * FROM members WHERE email='$email'";
$query = mysql_query($sqlCommand) or die (mysql_error());
$num_rows = mysql_num_rows($query);
if($num_rows>0){
$email_check = $row['email'];
$data['email'] = $email_check;
} else {
$data['error'] = "true";
}
echo json_encode($data);
Unfortunately, this process is not working at all. The registration form simply hangs endlessly, without validating nor passing the information to the database. We have done considerable research into how to properly code getJSON and the server-side php, but have come up against brick wall, so would very much appreciate any help or advice on this issue.
I hope this isn't too late, but after facing the same problem I found out the registration plugin doesn't send with the form object the preset details (name, email, gender, birthdate) -- only sends any custom fields. Quite dumb imo but could have an advantage or two.
Solution was to override the description of the email so as to trick it into thinking it's custom (which it would be) -- disadvantage being your regular checks will not be checked against (valid email et al) so you'll need to take care of those manually.
This may be late, but:
Change your JSON address to
http://www.mysite.com/register_check.php?email=' + form.email +
You were using facebook's check for existing user. Change:
cb({username: 'That email is taken'});
to:
cb({email: 'That email is taken'});
You want error message to appear next to email field.
I currently have a registration script on my website that requires the email to be from a specific domain.
So for this example, we will say that the user needs to signup using a #gmail.com email.
$gmail = 'gmail.com';
$emailCheck = strpos($_POST['email'],$gmail);
I then check to make sure the users form submitted email contains gmail.com before proceeding.
if($emailCheck === false) {
$err[]='Only #gmail.com email addresses are currently accepted.';
}
I only then proceed to continue with the registeration using a check to see if any $err[] have occured.
if(!count($err)) {
// INSERT IN DB, ETC. //
}
The issue I'm having is that if a user fills out user#yahoo.com the script provides and error. If the user provides user#gmail.com it works as it should. But if the user just inputs the first part of the email leaving out #gmail.com it also works, running the SQL query and inputing the values into the DB.
Any help would be greatly appreciated!
$input=trim($_POST['email']);
if (preg_match("/^\S+#gmail\.com$/i", $input) {
// INSERT IN DB, ETC. //
}
else {
$err[]='Only valid <your_login>#gmail.com email addresses are currently accepted.';
}
I want to limit the failed login attempts. For example, if a specific user attempt to login with wrong username or password 4 times, i should show the CAPTCHA 4th time instead of blocking for some specific time, and keep showing CAPTCHA unless he supplies valid username and password. Once the user has successfully logged in, the login attempt is reset to ZERO.
Is the idea of checking the username instead of IP address OK in security point of view? Can this approach be implemented without using database?, as I think I don't need to store time because i will just show recaptcha?
Please give your opinion.
You don't want to use the database for the 'number of failed logins'-check? Then just use a cookie and check it. Sure, they can remove it, but it's a hassle.
However, I suspect that you already are getting the username and password from the database, why not also fetch the last number of failed logins while you are at it?
if (isset($_POST['submit_login'])) {
if (isset($_POST['username']) && isset($_POST['password'])) {
$username = mysql_real_escape_string($_POST['username']);
$password = mysql_real_escape_string($_POST['password']);
// id = unique primary key
$rs = mysql_query('SELECT id,Username,Password,Failed_logins,IP_address FROM Users WHERE Username = '.$username.'');
$num = mysql_num_rows($rs);
if ($num > 0) {
// I would add a password hash here to $password, and check against the hashed Password from the database
// But let's check the clean passwords
$row = mysql_fetch_array($rs);
if ($password == $row['Password']) {
// Successful login, set session or whatever you need
// Reset failed logins
mysql_query('UPDATE Users SET Failed_logins = 0 WHERE id = '.$row['id'].'');
header('location: success.php');
} else {
// Failed password check
if ($row['Failed_logins'] > 3) {
// Redirect to captcha
header('location: captcha.php');
} else {
$ip = $_SERVER['REMOTE_ADDR'];
if ($row['IP_address'] != $ip) {
// New ip adress, reset failed logins
$failed_logins = 0;
} else {
// Increment failed logins
$failed_logins = $row['Failed_logins']+1;
}
mysql_query('UPDATE Users SET Failed_logins = '.$failed_logins.',IP_address = '.$ip.' WHERE id = '.$row['id'].' ');
} // End check Failed_logins > 3
}
} else {
// No such Username found in database
$error = 'no_such_username';
} // End username check from database
} else {
// Either username or password is missing
$error = 'incomplete_form';
} // end check for username and password
} // end main submit_login check
Something like that.
EDIT:
This is really old code and I see some problems with it now. But, at least you should always use PDO (prepared statements) for inserting data in your database.
What are you protecting?
Random user-only content, username.
Banking information (I doubt..) or other sensitive data, IP might be okay, provided you use ranges.
Are you asking how to do it, or which you should use?
You do need time, how would you otherwise determine if an login attempt has expired? If I would fail to login 4 times over a 10 year time span I would get blocked. You want to block those who attempt multiple login attempts in a short time span.
I suggest you use an database - as you will keep an detailed history of logins at the same time. But an memory based solution like memcached could also suffice.
To elaborate on which security to implement: a combination!
Combine the used username and ip address and store them into your database.
Use the login attempts on your username to directly protect your users from attempts.
Use the ip address information to observe an detect only, and take action if needed.
You should save the attempts in a database and check the user.
The best way to do this is to use databases.
You need to create a separate table in your database, which would store three variables :
(a) IP address (where the person is trying to log in)
(b) Number of login attempts
(c) date/time (or : current-timestamp)
The ONLY problem with this approach is with the first variable : IP address
What if the person is in an Internet Cafe? Or using public Wi-fi? Or Hotspot? Or, a school? office? etc, etc
If you block the IP address, it affects everybody who is trying to log into your website from that location.
This is not a problem if your website concerns something like a bank, or military installation, or the Pentagon.
But, if your website is a business (buy-and-selling, game, whatever), then blocking a specific address will only piss off your customers!