PHP Reset Password with code instead of link - php

Is there anything wrong with emailing a user a randomly generated code rather than a link when they want to change their password.
The procedure would be...
User triggers password reset by entering email address
Email is checked in DB to be a genuine user
Email is sent to that user with a randomly generated code
The same code is bcrypt'ed and inserted in the database in a dedicated column
User enters the code sent to them by email
It is checked against the stored based bcrypt password
(If all correct) User is allowed to enter a new password

There is really no difference in terms of security whether it's a code or a link.
All the link does is automatically enter the code and submit the form; the only downside of a link is that some (bad in my opinion) email providers would automatically issue GET requests to all links in emails, in which case what you should do is your link should lead to a page asking to click a button to confirm the reset (issuing a second POST request behind the scenes) so that "accidental" GETs don't trigger the reset.
In terms of security, email isn't the most secure medium as you have no way of enforcing encryption once the mail leaves your email server - if mail servers that happen to relay that email don't support encryption they'll pass the mail unencrypted to the next server, etc.
So assuming you're fine with the mail being potentially unencrypted during transit (not that you can do much about it - maybe using SMS but even that is unencrypted and I'm not really sure which one is harder to intercept), the only security you can enforce is to have the code entry page/link page served only over HTTPS so that an attacker sitting between the user's machine and your server won't be able to intercept it once the user attempts to enter the code or the link.
A good user experience solution is to make your code entry page accept query string parameters to fill in the code automatically if provided (and serve a blank form for the user to fill manually if the parameter is missing) and your email should both contain the (HTTPS) link and the code in case the user can't use the link for some reason.

Related

Hide parameter sent via POST request

I am making a registration wherein users inputs email as their username and I have to send a random code to their email after a successful registration.
So far, what I got is:
Step 1: User registers with their email.
Step 2: API Generates a random code and inserts it to my database table (tbl_temp_otp).
Step 3: After inserting to the database, the code will be sent to the email provided in the registration.
Step 4: User gets the code, and input it into the registration form then API checks if that code exists and is still in unused
state.
So far, it works.
My issue is that, how can I send the OTP code through POST request without anybody seeing it in the network tab in google chrome?
EDIT based on the comments
I think I explained the issue in a wrong way...
What I really meant was not without anybody seeing it but without the registrant seeing it.
Since, the registrant is able to see the code inserted in the database, I'm concerned that they'll register with a non-existing email address and verify it by looking into the network tab and input the inserted OTP code and still able to completely register with the provided non-existing email address.
There is no way to hide the OTP that is sent through a POST request. The best thing to do, is to make the OTP time-based, and limit the duration that it is valid for.
Anyways, the request that is visible in the network tab is only visible for the respective user, so there is no actual security risk. Hope this clarifies your question. You can continue with the same process.
You can't.
You are sending it by email where the email client needs to read it, then it needs to be transferred to the user's browser so the browser can send it back over the network.
The user's browser and email client belong to the user. They are under the user's control. They can't be used to conceal information from the user.
But that shouldn't matter. It is a one time code designed to prove that the user is who they claim to be. It doesn't matter if the user gets their hands on it.
There's a risk that it might be intercepted between the browser and your server, but you can mitigate that by using HTTPS.
There's a risk that it might be intercepted between the email client and the browser, but there isn't anything you can do about the possibility of the user having malware on their system which is designed to share your one time password. It's a pretty remote possibility in the first place though.
There's a risk that the email will be intercepted, but nothing you can do about that unless you want to force the user to invest in setting up and learning how to use GPG or similar. (Again, it is a small risk).
Re edit:
I'm concerned that they'll register with a non-existing email address and verify it by looking into the network tab
Your one-time code should be associated with the email address that it was emailed to.
It won't show up in the network tab until the user clicks the link in that email. So there is no way for them to get the code without the email address being real.
You've already associated the code with a particular email address in your database, so they can't change their mind and use that code with a different email address.

PHP / Mail / MySQL: Email Confirmation for Register

I'm not familiar with PHP / MySQL and Emails. And I'm pretty sure this question has been asked somewhere already, but I cannot find it. So I apologise if this is troubling and thank you in advance!
Is it possible to do something that user has to click on a link in email first before the user is added into database???
And you know how, for some websites, they have a unique web address for each email validation (Shown in red on the picture)? How do they create a webpage that's unique in for every email ?
Picture credited: https://kayako.atlassian.net/wiki/download/attachments/5734920/subs-validation.png?version=1&modificationDate=1291956283000&api=v2
Thank you a lot for the attention! If it's possible, I prefer not having straight scripts that I can copy and paste because I like to find out myself :P But please do give me some hints because I'm totally lost.
If there's anything that's not clear, please tell me, I'll try my best to clarify it!
The Registration process
User fills out a form online with basic details including an email and password, and submits the form to register.php
register.php adds user info to a temporary location, such as a pending_users table which has all the fields the user submitted along with an expiration and an activation_code fields. This code can be any random, impossible to guess value. eg: hash('sha1', mt_rand(10000,99999).md_rand(10000,99999)). Just don't do anything predictable such as hash the current time, or the username
register.php sends an email to the user with a URL that will link to activate.php and that includes the activation code. eg: example.com/activate.php?code=a2ef24... The email should also inform the user of the expiration (1 to 12hrs validity seems ok to me)
When user clicks the link, she triggers a GET request to activate.php. In doing so, the user proves ownership of the email address
activate.php gets the code from the request parameters, eg: $code=$_GET['code']. With that code, the script queries the pending_users table for the record matching that code.
If the code is found, check that it hasn't expired before proceeding. Expiration prevents someone else much later who gets in the user's account from completing the registration.
If the code is valid, capture the user details from the matching record and delete that record from pending_users table.
Write a matching record in the regular users table. Until this is done, the user could not log in because login script only checks the users table, and ignores the pending_users table.
Registration complete.
Security Note I:
For your users' protection, never store passwords in cleartext. When you receive it from the registration form (eg: $_POST['pwd'], do:
$pwd = $_POST['pwd'];
//first validate; it should meet minimum requirements
$pwd_hash = password_hash($pwd, PASSWORD_DEFAULT); // <- the hash gets stored
Later, to verify the password, do:
password_verify($cleartext_pwd, $pwd_hash);
It will return true if the password is correct; false otherwise.
Security Note II:
For your protection, never insert user supplied values directly in your DB queries. This means any value that arrives from the outside. Not just usernames, emails, passwords... but also values that you're getting back from the user such as activation_code above or cookie values or headers (eg User-Agent). Instead, learn to use prepared statements. This will protect you from SQL injection.
Not sure if it's possible to add datas in database after the validation...
When I want to do something like that, I create a data in the users table (or metas users table) like "validate".
If this data is "true", then the user already did the validation and he can use his account. If it's still set on "false", the user didn't validate his account : he can't use it.
With that, you have to make sure the account is validate when the user tries to log in, but it's not a big deal ^^
Hope it's usefull.
Those are not a unique websites, there is only one script validating the registration finalization. The incoming requests (when the user has clicked the link) are routed all to the same script by means of server side "request rewriting", so that the random token value is available as an argument (parameter) to the script execution.
What the script does: it checks if that random token value does exist in the database where it has been generated and stored before when the user actually registered.
The only thing left to do for that script is to remove the confirmation random token and/or set a flag indicating that the registered use has actually confirmed his identify (email address) by clicking the link.
Easy and straight forward. Hard to bypass, since you cannot guess what random token value has been generated for what registered user without receiving the email. However take into consideration that it is trivial for an attacking script to use anonymous email services (one time email addresses) to receive and evaluate such a confirmation request, if the process is known to the attacker.

Secure account registration

I'm writing a web application and security is very important to me. When writing the 'forgot password' function I have taken the following advises into account:
Generate secure random auth token
Send user an email with this token
Send user an SMS with a pin code to enter in combination with the email link
Do not leak account info, the site will not mention an unknown email address when the user enters an unknown address. This way accounts cannot be linked to the webapp.
But this made me thinking. The website requires the user email address to be unique. Therefore the register form notices the user when the email already exists.
This means my attempts to not leak account existence information to an attacker in the 'password recovery' part are useless because I leak the same information in the register part of the site.
I have been looking at ways to prevent this and best practices around this, but not much information is written about it.
I have also noticed the same issue in the stackoverflow account system. Stackoverflow does not leak this information in the 'forgot password' function but it does leak account existance information in the registration process.
Can anyone point me in the right direction on this one?
Kind regards,
Daan
Update
A few minutes after typing this I had the following idea on how not to leak information to an attacker:
Do not present the new user with the message 'email address is in use', but accept the registration as if it would be a new one.
When the email address already exists, just send an email with the message there is already an account for that email address.
What do you think?
if you do password recovery, always send an alert that the email has been send when the email is legit, but only send when he is in your database.
In the registration part it's required to say if the e-mail exists in the database, so leakage is possible but for people to guess e-mail addresses without knowing them they need to bruteforce it which can be avoided by limiting the amount of posts the users can do to prevent spamming
There is always the possibility to move towards Login Names for logging in. You don't quite need your email address to be unique unless that is the sole method of logging in.
The answer her would just be more obfuscation if you realistically want to be more secure. What you could have:
Login name (the actual unique identifier to login to your web application, has to be unique)
Display name (the name visible to other users, does not have to be unique)
This could make guessing login names more difficult, and it would be slightly more secure. Email addresses would not need to be unique necessarily. But then the user must always keep track of what login name they signed up with. This process could ostensibly hide this information from attackers. Since both login and password recovery would ask for the username only.
I don't think this problem has a great answer. Like Joachim pointed out, you are always prone to leaking when you require the email to be unique. Since checking has to be done upon registration. Password recovery can be managed simply by always giving the message "Instructions have been sent to the email you provided" whether or not the email exists in your database. And failure or success would be handled internally but never be shown. Best way to make timing attacks impossible is defer the whole sending process to be asynchronous from your HTTP response.
So options are either, move to a login name system (with the same security holes ostensibly) or just accept that with unique email addresses you will leak during the registration process.
There might not be a best answer here. Part of security is getting users to be clever. And signing up with just an email address that is not really public, tends to help. But if an attacker has access to their sign up email, things are out of your control. And there is not much you can do (unless you have two step auth with SMS or something)

Verifying an unverified email address?

Say a user registers on a site with an email address that needs to be verified first before accepting the user's registration.
The general approach is to send an email to the email address provided. The user then checks his/her inbox and clicks a link that would tell the site that the email address is valid. Usually, the link would have some sort of code embedded in it that tells the site whether it's a legit validation of the email address.
My question is about the code. What's the best way to implement it? Some ideas:
A random string is generated when a new address is entered into the site. This random string is stored in the the DB and then emailed to the registrant. The link in the email will contain the random string as part of the URL.
The email addressed is hashed. This means nothing needs to be saved in the database because the application will know how to unhash this. (My concern with this approach is if the user later changes his email address to something he previously entered, the hash would be the same. Not sure if this poses some sort of security threat.)
Some other approach?
I'm looking for general advice to this problem.
Number one, the random string, is the safest (and AFAIK, the most common) way to go: It makes it completely impossible to maliciously do things to E-Mail addresses that I haven't received the E-Mail for.
Also, you can remove the random string after successful completion, making it clear that that particular address doesn't need activation any more.
i'm using 1st approach every time
Create a random code
Store it at DB
Mail it to user with clickable link like activation.php?key=blabla
At activation page let user able to enter it manually too
At submit compare the keys
Activate user
If user change mail adress return 1

User registration (and authentication later) - my method or use OpenID?

Good afternoon,
I am working on a script to allow news users to register on a website.
In a nutshell, these are the steps I have planned:
register.php - New user completes a form, entering their username, address details, business name and email address. The data is then posted back to the script via SSL.
register.php - The script checks that the username or email address is not already stored in the database. If they are not, it uses those data to generate a token, which is emailed to the email address in the form of a hyperlink, with that token and the rest of the data as parameters of the hyperlink. The token used is made from a secret string - that way, only this script can make a code that can be reconstructed using the rest of the data.
email - The hyperlink (SSL) is clicked on, thereby passing the data via $_GET into the next script via SSL.
verify.php - The token is reconstructed using the passed $_GET data and a known secret string. If the hash is identical, we know that the token was generated by one of our scripts. The user is prompted to enter a password (twice), before clicking "submit" (which posts the data to itself, via SSL).
verify.php - The script checks if the username or email address does not exist, before inserting the new user data into the database, along with a hashed password and salt.
email - An email notification is sent to the administrator, to tell them that a new user has registered - the new user needs to be approved before they can log in. The email contains a link to the next script, with the ID of the new user passed to it via $_GET. SSL is used.
confirm.php - The script uses the passed ID of the new user to display all details that have been registered, in editable fields (not the password or salt). On clicking "confirm", the form data is posted back to the same script, via SSL.
confirm.php - The script updates the record for that user, and sets the new user record to "confirmed". The new user receives email notification, and can now log in.
This may seem long, but there are a series of steps that need to be completed.
All new users must verify their email address before any data are stored in our database. The password is not passed about any more than it needs to be. It is only passed in its raw form back into the "verify.php" script via POST, which then hashes it. I will ensure that POST data for SSL packets are not logged on the server. In that way, there should be no record of the raw password on the server, right?
A random salt for each user is generated and stored - to protect against rainbow tables.
Have I missed something? My only concern is the transmission of the raw password via SSL. Although SSL protects against sniffing, I am still uneasy about receiving the raw password into the server. That said, I do not want to make the project vulnerable to "middle-man" attacks, by hashing it client-side.
Can anyone suggest any flaws in my method? I tried Googling this, and although there are some applicable posts, nothing seems to tie in the whole process. I hope this thread will benefit future visitors to this page as well as myself.
Thanks.
I had to do the same thing 2 months ago and I did it your way. Except for this point:
Before asking the user to enter everything, the first step should be to validate and confirm the email. Once completed, we ask everything else.
2 goals are reached:
Users are sometimes afraid to enter too much info.
If they have already sent their email, they are usually more willing to continue the process.
Users that already signed up and that are in the wrong page: you can suppose they lost their email and propose a solution (if the email is already in the database)
...But I personally think the best thing is to stick with openId ;-) Next time that's what I will try to use.

Categories