My PHP codeigniter site sends 3 types of emails to users, they are html formatted and I wish to have a 'view this email in browser' link option.
How can I create a reasonably secure link? 2 of the emails are sensitive in nature:
New Registration: email shows their details and an activation link
Forgot Pass: email shows the link to reset pass
Is it enough to just do something like this?:
$code = sha1(mt_rand(10000,99999).time().$user_email);
$link = 'email/view/' . $code . '/' . $user_id . '/';
=> http://mysite.com/email/view/c0acc09c6d00b706e1e511e52f286b1859067047/213/
So authentication would be done based on a random hash and their user_id. Is it worth hashing the user id also?
I wouldn't include the user_id as a URL parameter, I'd just stick with the hash.
As soon as somebody navigates to the link, the hash should be cross-checked with those stored in your database, and if they match, pull the information associated with that e-mail (i.e. username, e-mail). When they go to fill out the form you can validate to see if their entered data is compatible.
For example, if a user has forgotten their password, as soon as they enter their e-mail address I reset their password to a random value (stored as a hash) and use this stored hash as the URL parameter in their reset e-mail.
This should be fine, I'd add that once the sensitive pages have been viewed, it'd be a good job to remove those pages from working with that hash a second time - when you send the email have the hash stored in the db, and when the page is viewed check the hash exists, if so allow the page view, and remove the hash from the db so it would fail the check if the page is viewed a second time. (assuming they wont ever need to view the same link twice from that email).
Related
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.
I'm trying to implement a password forgot page in my website. I just would like to know if my idea is correct. User enter his email address, i save on a database his IP, timestamp, and an id for a "random password change" page. I create this "random password change" page with fopen();. Once user clicks on the email link i check if page should be expired ( ex 30min ) if it's expired i redirect to user to a "sorry too late" page where i delete the "random password change page" with unlink(), if it's not expired i let user change his password, redirect it to "password changed" page and from there i remove the "random password change" page with unlink().
Side effect of this... if user doesn't click on the email link my random page will never be deleted.
What do you think of this ? Is that a good practice ?
UPDATE
Hi everybody ! Thanks all of you for your help ! Everything seems to run smooth now :)
There's no need to have a dedicated file for each confirmation. The confirmation code can be passed as a query parameter in the URL:
http://example.com/verifyme.php?confirmation=XXXXX
The script would retrieve it via
$code = $_GET['confirmation'];
and then the confirmation/deactivation business takes place in the database. The verifyme page would always be present, but simply not do anything unless a code is passed in.
Better way in my opinion is to generate random hash associated with user's email and creation timestamp.
Then send to the user an url like:
http://example.com/activate.php?email=my#email.com&token=fdeW3tx
Then check in this file if email and token exist and if current time is less than creation time + 30 minutes.
If so, pass activation.
Better approach:
Create one php page forgotPassword.php.
If a user forgots its password, save the following information:
activationid, userid, random-activationcode, timestamp
Now send out a link forgotPassword.php?activationid=1234&random-activationcode=56789
If the user clicks on the link, open the page and present a form where the user can reset its password. If the page is expired (compare with timestamp) present a "page not available" message instead.
Best,
Christian
No, it is no good idea to actually create a page in the file system. You have your database to store the information.
You save the email address (or any other identifier of the user – I would not suggest to use the IP), timestamp and secret key (you called it id) to the database. Then you send an email to the user containing a link like changepass.php?email=<address>&key=<key>.
When the user opens this URL, you get the email address and key as parameters and can check your database, whether the email and key are matching and the timestamp is not too old. If this is ok, you allow the user to change his pw. If this is not ok, you show an error message.
Please don't save a file to your server!
You should store the user's id, the timestamp and a token in the table of password requests. Additional you send the user a mail with a link including the token as a get parameter
(e.g. www.domain.com/forgottenpassword.php?token=CK32A8).
This requested page should offer a form to enter new password. Getting a request from this page the server check both the token (must me the same) and the time (actual < saved + delay) using the database informations.
Token Generation
Don't use a hash of username and timestamp. This way, an alien could create a token by himself. Of course it is unlikely, it always is. :-) So use a random string and hash it or combine a random string with user informations if you want.
i need to make activation code for my users. When users are registered, my php script would send an email to users and i dont know how to implement activation code or activation link. I dont know logic for this
This is a four-step process:
Create the activation code
Store it in a database
Email the code to the user with a link to your verification script
Check the code the user enters in your verification script against the value stored in the database.
For an example implementation, please see:
http://www.learnphponline.com/scripts/email-activation-for-php-forms
upon registration create a random string $user_rand;
store the random string in the users table in activation_secret column, set the active column to 0
hash the random string and send an email to the email address the user provided and include a link to your activation page, include the hash as a parameter. e.g. http://host.com/activate.php?activation_code=sfer3423ste&username=john
in activatate.php extract the username and the activation code (which is the hash you sent)
query users table for a record which has active=0 and user=john, return the value in activation_secrete
hash activation_secrete and compare the hash with activation code from the url, if they match, the user should be validated (set active column to 1) if not, inform the user the activation code is not recognized.
You can build on this and make it robust and add exception handling. E.g. you can also set a life time for the activation secrete and more.
I've not done it but I would have thought that it would be along the lines of:
When a user registers generate an activation code, and store it associated with the users id
Have a page which validates a code. this will look up the code given in the url (or have the user enter it manually in a field on the page) and see if it is the code associated with the user (must be logged in to see this page)
Generate a url which goes to the above page and provides the code in the url.
insert the url in an email and send to the user
Or you could simply use CakeDC's users plugin and avoid all that trouble.
You can also use a table-less solution to generate one-time passwords. Have a look at http://bakery.cakephp.org/articles/ashevat/2010/03/12/how-to-implament-one-time-password-for-forgot-my-password-and-account-activation-processes
Working on a web based "buying and selling" application with PHP MySQL where users can post adverts for items and services.
Before a new advert is displayed on the system there must be a method of verification to ensure that the user provided email address is correct, and that the advert is legitimate.
I want to send the creator of any new advert an email containing an url which directs to a page whose primary functionality is to receive a posted variable, $advert_id, and to select the advert from the db for updating / editing / deleting.
This variable is embedded in the url with PHP syntax
ie. [http://www.example.com?content=modify_advert&advert_id=2246317].
This part is quite simple to implement, BUT, if a user was to modify this variable called "advert_id=2246317" to any other integer, they can access other posts/adverts in the system.
The system is advert based, and users dont need an account or login to post, so we cannot prompt for a login at the point of verification which would have been convenient.
Any ideas as to how we could protect the adverts/posts in the system from being accessed via the aforementioned url???
Any suggestions?
If visitors will only be viewing that page from the link you send via e-mail, you can include a hash in that address instead of the advert_id — essentially a random, one-time password.
One common and "often good enough" trick for generating such a random password is to take a single, secret, truly random string (I usually use grc.com), concatenate it with the unique advert_id, and hash the whole thing with, say, SHA1(). Like so:
UPDATE advert SET advert_hash = SHA1(CONCAT(advert_id, 'lots-of-randomness-here'))
You could even vary this by adding time(), or (better still) a random number to the end. The outcome is a 40-character string stored in your database that nobody could possibly predict (without knowing the secret data you used to generate it).
For example, I might get this instead of advert_id=1:
f2db832ddfb149522442c156dadab50307f12b62
If I wanted to sneakily edit advert_id=2 (which somebody else created), I'd first have to guess that the hash is this completely different string:
e5c6a3a9473b814b3230ee7923cbe679fcebc922
So, include that in the URL instead of the advert_id (or, if you like, in addition to the advert_id), and suddenly your users are powerless to ruin other people's content.
You could add a salt to the id and then hash it.
sha1($advert_id . $salt);
Send this to the user in the URL instead of the advert_id, and store it in your database, along with the advert_id.
Then when they click the link, you find the matching advert for that hashed value.
Making the salt a secret is how you keep users from 'guessing' a valid URL that will let them modify an ad that they did not post. Perhaps you could use the users email address, the time posted and/or a name or something that the user enters when they make a post.
Generate a GUID as the advert ID so simple ID guessing attacks are unlikely to succeed.
I have a new website. And the following is my scenario:
I will send an email to 5 people (numbers not important), inside the email, i will include a link for them to click:
www.domain.com/email=abc#xyz.com&key=abc...xyz
They key are randomly generated using salt and sha1 in php. Upon click the link in their email, can I directly let them access the update profile page?? Or do I need to ask them login again?
If I directly let them access the update profile page, what are the security things I need to take care? I know the use of login, can store session, but, the thing is, they click the link from their email, and I think its quite private and safe.
The only security flaw I can think of is: the hacker can magically memorize the "key" (which is about 60++ characters), and then type in browser URL: www.domain.com/email=abc#xyz.com&key=abc...xyz.
If the hackers can do that, then I am done. My users account will be hacked.
Is there anything else that hacker can hack? Just update profile page only.
Btw, if they already update their profile, should I remove the "key" in database??
I am using php and mysql
A password reset email should have a one-time use - store an opaque token in your database, send it in the email, and only allow it to be used once.
I agree with Paul, but for profile updating I suggest to do it after login.
You can also display and memorize the ip address of client when he resets his password.
Typical practice is to require a user to change their password when they are sent a 'Forgot Password' email, and then make them log in before they can change anything.
A recent implementation of a password email that I created worked as follows:
Create an array containing the id of the user, and the current timestamp.
Serialize and then encrypt the resulting string (using a symmetric key, which is stored on your server).
Put that encrypted string in a url parameter (my advice is to base64_encode the data twice in order to ensure you don't get bad characters in the url), and then send it to them in an email.
When someone clicks on the link in their email, first check that the parameter decrypts properly (meaning it's valid), and then deserialize the data structure. You now check that original timestamp. If too much time has passed since that point, reject the forgotten password url as too old.
If the url is valid, and recent enough, take them to a 'reset password' page.