Reputation: 2115
I am currently implementing a forgot password function in a Java project. my methodology is,
Although I have restricted the email address
field in the reset password page from editing (a read only field) any one can alter the url in the address bar of the browser and change the email address field.
How Do I restrict every user from altering the email address in the reset password page?
Upvotes: 21
Views: 61046
Reputation: 468
If You are looking for the complete code to implement forgot password, here I share my code. Put the link where you need.
<button> <a href="forgotpassword.jsp" style="text-decoration:none;">Forgot
Password</a></button>
Below is my forgotpassword.jsp
page.
<form id="register-form" role="form" class="form" method="post"
action="mymail_fp.jsp">
<h3>Enter Your Email Below</h3>
<input id="email" name="email" placeholder="Email address" class="form-
control" type="email" required autofocus>
<input name="recover-submit" class="btn btn-lg btn-primary btn-block"
value="Get Password" type="submit">
</form>
Once the email is submitted, then it is redirected to mymail_fp.jsp
page where i send the email to the user.
Below is mymail.jsp
page.
<%
mdjavahash md = new mdjavahash();
String smail =request.getParameter("email");
int profile_id = 0;
if(smail!=null)
{
try{
// Register JDBC driver
Class.forName("com.mysql.jdbc.Driver");
// Open a connection
Connection conn =
DriverManager.getConnection("jdbc:mysql://localhost:3306/infoshare", "root",
"");
Statement stmt = conn.createStatement();
String sql1;
sql1="SELECT email FROM profile WHERE email = '"+smail+"'";
ResultSet rs1=stmt.executeQuery(sql1);
if(rs1.first())
{
String sql;
sql = "SELECT Profile_id FROM profile where email='"+smail+"'";
ResultSet rs2 = stmt.executeQuery(sql);
// Extract data from result set
while(rs2.next()){
//Retrieve by column name
profile_id = rs2.getInt("Profile_id");
}
java.sql.Timestamp intime = new java.sql.Timestamp(new
java.util.Date().getTime());
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(intime.getTime());
cal.add(Calendar.MINUTE, 20);
java.sql.Timestamp exptime = new Timestamp(cal.getTime().getTime());
int rand_num = (int) (Math.random() * 1000000);
String rand = Integer.toString(rand_num);
String finale =(rand+""+intime); //
String hash = md.getHashPass(finale); //hash code
String save_hash = "insert into reset_password (Profile_id, hash_code,
exptime, datetime) values("+profile_id+", '"+hash+"', '"+exptime+"',
'"+intime+"')";
int saved = stmt.executeUpdate(save_hash);
if(saved>0)
{
String link = "http://localhost:8080/Infoshare/reset_password.jsp";
//bhagawat till here, you have fetch email and verified with the email
from
datbase and retrived password from the db.
//-----------------------------------------------
String host="", user="", pass="";
host = "smtp.gmail.com"; user = "[email protected]";
//"email@removed" // email id to send the emails
pass = "xxxx"; //Your gmail password
String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
String to = smail;
String from = "[email protected]";
String subject = "Password Reset";
String messageText = " Click <a href="+link+"?key="+hash+">Here</a> To
Reset
your Password. You must reset your password within 20
minutes.";//messageString;
String fileAttachment = "";
boolean WasEmailSent ;
boolean sessionDebug = true;
Properties props = System.getProperties();
props.put("mail.host", host);
props.put("mail.transport.protocol.", "smtp");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.", "true");
props.put("mail.smtp.port", "465");
props.put("mail.smtp.socketFactory.fallback", "false");
props.put("mail.smtp.socketFactory.class", SSL_FACTORY);
Session mailSession = Session.getDefaultInstance(props, null);
mailSession.setDebug(sessionDebug);
Message msg = new MimeMessage(mailSession);
msg.setFrom(new InternetAddress(from));
InternetAddress[] address = {new InternetAddress(to)};
msg.setRecipients(Message.RecipientType.TO, address);
msg.setSubject(subject);
msg.setContent(messageText, "text/html");
Transport transport = mailSession.getTransport("smtp");
transport.connect(host, user, pass);
%>
<div class="alert success" style="padding: 30px; background-color: grey;
color: white; opacity: 1; transition: opacity 0.6s; width:50%; margin: 10%
5%
15% 20%;">
<a href="forgotpassword.jsp"> <span class="closebtn" style="color: white;
font-weight: bold; float: right; font-size: 40px; line-height: 35px; cursor:
pointer; transition: 0.3s;">×</span> </a>
<h1 style="font-size:30px;"> <strong>Check Your Email. Link To
Reset Your Password Is Sent To : <%out.println(" "+smail); %></strong>
</h1>
<center><a href="forgotpassword.jsp"><h2><input type="button" value="OK">
</h2></a></center>
</div>
<%
try {
transport.sendMessage(msg, msg.getAllRecipients());
WasEmailSent = true; // assume it was sent
}
catch (Exception err) {
WasEmailSent = false; // assume it's a fail
}
transport.close();
//-----------------------------------------------
}
}
else{
%>
<div class="alert success" style="padding: 30px; background-color: grey;
color: white; opacity: 1; transition: opacity 0.6s; width:50%; margin: 10%
5% 15% 20%;">
<a href="forgotpassword.jsp"> <span class="closebtn" style="color:
white; font-weight: bold; float: right; font-size: 40px; line-height: 35px;
cursor: pointer; transition: 0.3s;">×</span> </a>
<h1 style="font-size:30px;"> <strong>There Is No Email As
Such <%out.println(" "+smail); %></strong>Try Again </h1>
<center><a href="forgotpassword.jsp"><h2><input type="button"
value="OK"></h2></a></center>
</div>
<%
}
stmt.close();
rs1.close();
conn.close();
}catch(SQLException se){
//Handle errors for JDBC
se.printStackTrace();
}catch(Exception e){
//Handle errors for Class.forName
e.printStackTrace();
}
}
else{
%>
<div class="alert success" style="padding: 30px; background-color: grey;
color: white; opacity: 1; transition: opacity 0.6s; width:50%; margin: 10%
5% 15% 20%;">
<a href="forgotpassword.jsp"> <span class="closebtn" style="color: white;
font-weight: bold; float: right; font-size: 40px; line-height: 35px;
cursor:
pointer; transition: 0.3s;">×</span> </a>
<h1 style="font-size:30px;"> <strong>Please Enter The Valid
Email Address</strong> </h1>
<center><a href="forgotpassword.jsp"><h2><input type="button" value="OK">
</h2></a></center>
</div>
<%
}
%>
Now what i have done here is, before sending email sending email to the user, I save sent time, expire time, generate random number from 0 to 1000000 and concatenate with sent time and encrypt is and send it as query string in the link in the email. So email will be sent and link to password will be sent along with the hash key. Now when user clicks on the link, they are sent to reset_password.jsp and following is reset_password.jsp
page.
<%
String hash = (request.getParameter("key"));
java.sql.Timestamp curtime = new java.sql.Timestamp(new
java.util.Date().getTime());
int profile_id = 0;
java.sql.Timestamp exptime;
try{
// Register JDBC driver
Class.forName("com.mysql.jdbc.Driver");
// Open a connection
Connection conn =
DriverManager.getConnection("jdbc:mysql://localhost:3306/infoshare", "root",
"");
Statement stmt = conn.createStatement();
String sql = "select profile_id, exptime from reset_password where
hash_code ='"+hash+"'";
ResultSet rs = stmt.executeQuery(sql);
if(rs.first()){
profile_id = rs.getInt("Profile_id");
exptime = rs.getTimestamp("exptime");
//out.println(exptime+"/"+curtime);
if((curtime).before(exptime)){
%>
<div class="container">
<form class="form-signin" action="update_reset.jsp" method="Post">
<br/><br/>
<h4 class="form-signin-heading">Reset Your Password Here</h4>
<br>
<text style="font-size:13px;"><span class="req"
style="color:red">* </span>Enter New Password</text>
<input type="password" id="inputPassword" name="newpassword"
class="form-control" placeholder="New Password" required autofocus>
<br>
<text style="font-size:13px;"><span class="req"
style="color:red">* </span>Enter New Password Again</text>
<input type="password" id="inputPassword" name="confirmpassword"
class="form-control" placeholder="New Password Again" required>
<input type="hidden" name="profile_id" value=<%=profile_id %>>
<br>
<button class="btn btn-lg btn-primary btn-block"
type="submit">Reset Password</button>
</form>
</div> <!-- /container -->
<% }
else{
%>
<div class="alert success" style="padding: 30px; background-color:
grey; color: white; opacity: 1; transition: opacity 0.6s; width:50%;
margin: 10% 5% 15% 20%;">
<a href="forgotpassword.jsp"> <span class="closebtn"
style="color: white; font-weight: bold; float: right; font-size: 40px;
line-height: 35px; cursor: pointer; transition: 0.3s;">×</span>
</a>
<h1 style="font-size:30px;"> The Time To Reset
Password Has Expired.<br> Try Again </h1>
<center><a href="forgotpassword.jsp"><h2><input type="button"
value="OK"></h2></a></center>
</div>
<%
}
}
else{
%>
<div class="alert success" style="padding: 30px; background-color: grey;
color: white; opacity: 1; transition: opacity 0.6s; width:50%; margin:
10% 5% 15% 20%;">
<a href="forgotpassword.jsp"> <span class="closebtn" style="color:
white; font-weight: bold; float: right; font-size: 40px; line-height:
35px; cursor: pointer; transition: 0.3s;">×</span> </a>
<h1 style="font-size:30px;"> The Hash Key DO Not Match.
<br/> Try Again!! </h1>
<center><a href="forgotpassword.jsp"><h2><input type="button"
value="OK"></h2></a></center>
</div>
<%
}
// Clean-up environment
rs.close();
stmt.close();
conn.close();
}catch(SQLException se){
//Handle errors for JDBC
se.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}
%>
In this page i fetch hash key and compare with database hash key and it it is true, then i fetch expire time and compare with current time. If time to reset password has not expired then i show form to reset password otherwise i throw error message. If time has not expired then i show form and when the form is submitted, it is redirected to update_reset.jsp
and following is my update_reset.jsp
page.
<%
mdjavahash md = new mdjavahash();
String profile_id= request.getParameter("profile_id");
String np= request.getParameter("newpassword");
String cp = request.getParameter("confirmpassword");
//out.println(np +"/"+ cp);
if( np.equals(" ") || cp.equals(" ")){%>
<div class="alert success" style="padding: 30px; background-color: grey;
color: white; opacity: 1; transition: opacity 0.6s; width:50%; margin: 10%
5% 15% 20%;">
<a href="reset_password?profile_id=<%=profile_id%>"> <span
class="closebtn" style="color: white; font-weight: bold; float: right;
font-size: 40px; line-height: 35px; cursor: pointer; transition:
0.3s;">×</span> </a>
<h1 style="font-size:30px;"> Please Fill Both The Fields
</h1>
<center><a href="reset_password?profile_id=<%=profile_id%>""><h2><input
type="button" value="OK"></h2></a></center>
</div>
<% }
else if(!np.equals(cp)){
%>
<div class="alert success" style="padding: 30px; background-color: grey;
color: white; opacity: 1; transition: opacity 0.6s; width:50%; margin: 10%
5% 15% 20%;">
<a href="reset_password?profile_id=<%=profile_id%>"> <span
class="closebtn" style="color: white; font-weight: bold; float: right;
font-size: 40px; line-height: 35px; cursor: pointer; transition:
0.3s;">×</span> </a>
<h1 style="font-size:30px;"> The Two Passwords Do Not
Match. Try Again </h1>
<center><a href="reset_password?profile_id=<%=profile_id%>"><h2>
<input type="button" value="OK"></h2></a></center>
</div>
<%
}
else{
try{
// Register JDBC driver
Class.forName("com.mysql.jdbc.Driver");
// Open a connection
Connection conn =
DriverManager.getConnection("jdbc:mysql://localhost:3306/infoshare",
"root", "");
// Execute SQL query
Statement stmt = conn.createStatement();
stmt.executeUpdate("update profile set
password='"+md.getHashPass(np)+"' where Profile_id="+profile_id+"");
//response.sendRedirect("mainpage.jsp");
%>
<div class="alert success" style="padding: 30px; background-color:
grey; color: white; opacity: 1; transition: opacity 0.6s; width:65%;
margin: 10% 5% 15% 20%;">
<a href="login.jsp"> <span class="closebtn" style="color: white;
font-weight: bold; float: right; font-size: 40px; line-height: 35px;
cursor: pointer; transition: 0.3s;">×</span> </a>
<h1 style="font-size:30px;"> The Password Is
Successfully Reset.<br> Try Login With New
Password</h1>
<br><br><center><a href="login.jsp"><p style="font-size:20px">
<input type="button" style="width:40px; height:35px;"
value="OK"></p></a>
</center>
</div>
<%
stmt.close();
conn.close();
}catch(SQLException se){
//Handle errors for JDBC
se.printStackTrace();
}catch(Exception e){
//Handle errors for Class.forName
e.printStackTrace();
}
}
%>
In this page i validate the fields first and then i update the database with the new password. Though it is long but it works. I have used MD5 encryption technique here and if you want how to do then follow the link How to Use MD5 Hash for securing Login passwords in JSP with Javascript?
Upvotes: 2
Reputation: 8849
This question has been posted 3 years before this answer... Yet I think it might be helpful to others.
So in short: I totally agree with your flow. Looks very secured, and your only open end makes also sense - how can you make sure that no one changes the username, and by that can set a new password for him.
I like less the idea of storing temporarily things is the DB (as the accepted answer suggests).
The idea I was thinking about was to sign the data in the link that is sent to the user. Then, when the user clicks the link and the server receives the call, the server also gets the encrypted part and can validate that the data was untouched.
By the way (here comes a "promotion"): I have implemented a JAVA project for these use cases (also "create account", "change password" etc.). It is free on GitHub, open source. It answers your question perfectly... implemented in Java, on top of Spring Security.
There are explanation for everything (and if something is missing - let me know...)
Have a look: https://github.com/OhadR/oAuth2-sample/tree/master/authentication-flows
See a Demo here.
There is also a client web-app that uses the auth-flows, with the README with all explanations: https://github.com/OhadR/Authentication-Flows
Upvotes: 2
Reputation: 179
I agree with the answer given by @clement if you HAVE to implement the forgot password functionality yourself. Sounds like a reasonable and secure way for this implementation.
However, as an alternative, if you don't have to implement it yourself I would suggest to use a service that does this for you, like Stormpath.
If you decide to use Stormpath, the code that would trigger the functionality would look like this in Java (with Stormpath's Java SDK):
Account account = application.sendPasswordResetEmail("[email protected]");
Your user would get an email with a link like:
http://yoursite.com/path/to/reset/page?sptoken=$TOKEN
And then, when the user clicks on the link, you would verify and reset the password like this:
Account account = application.resetPassword("$TOKEN", "newPassword");
The details about how this works can be found in the Stormpath's password reset documentation.
With this approach you don't have to implement and maintain the functionality for yourself if you have the option not to do so.
Note: Stormpath has joined Okta.
Upvotes: 3
Reputation: 1204
There are two common solutions:
1. Creating a new password on the server and inform user from it.
2. Sending a unique URL to reset password.
The first solution has a lot of problems and is not appropriate to use. These are some reasons:
1. The new password which is created by server should be sent through an insecure channel (such as email, sms, ...) and resides in your inbox.
2. If somebody know the email address or phone number of a user who has an account at a website then then it is possible to reset user password.
So, second solution is better to use. However, you should consider following issues:
- The reset url should be random, not something guessable and unique to this specific instance of the reset process.
- It should not consist of any external information to the user For example, a reset URL should not simply be a path such as “.../?username=Michael”.
- We need to ensure that the URL is loaded over HTTPS. No, posting to HTTPS is not enough, that URL with the token must implement transport layer
security so that the new password form cannot be MITM’d and the password the user creates is sent back over a secure connection.
- The other thing we want to do with a reset URL is setting token's expiration time so that the reset process must be completed within a certain duration.
- The reset process must run once completely. So, Reset URL can not be appilicable if the reset process is done completely once.
The common solution can be generating a URL to create a unique token which can be sent as URL parameter, it contains a URL such as “Reset/?id=2ae755640s15cd3si8c8i6s2cib9e14a1ae552b”.
Upvotes: 1
Reputation: 4266
You have to save it in DB before sending email by using token:
email
, token
, expirationdate
token
in the Url, server can identify the user
, check if request is not expired thanks to expirationdate, put right email into the box, and ask for password renewal. User type new passwords and you have to give the token (hidden field
in the form) + passwords to the server. Server don't care about the textbox for the email because with the token, user is identified strongly
expirationdate
(again), check if password match
and if all is ok, save new password! Server can send again message in order to inform user that password has been changed due to the request.This is really safe. Please use short time for the expirationdate
to improove the security (for instance 5 minutes is correct for me) and use strong token (as GUID, see comments)
Upvotes: 42
Reputation: 4659
You cant restrict email address to being change by user.
Email address can easily change by editing source code in browser even though you have taken hidden or make text box as readonly.
You may provide uniq random string or token
with reset link and
check the email address and token combination after clicking on reset password link or after user submitted request for the reset password by checking email address and token string in request with the email address and token string in your database.
If email address is present in your database that means email address is valid, if not than give message that email address doesn't exist in you user records.
NOTE :
If your are using any framework or simply servlet than it better to provide link, so that you can validate email and token string before you display your reset password form. If token string or email address in invalid than you can restrict user from submit request for reset password and validate after submitting request. It will more secure than validating after submitting reset password request.
Upvotes: 2