Implementing forgot password functionality in Java

52,402

Solution 1

You have to save it in DB before sending email by using token:

  1. When user click on "send me a email with reset instructions", you create one record in DB with those fields: email, token, expirationdate
  2. User receive email with yourwwebsite.com/token and click on it
  3. With the 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
  4. Then server check if token still valid with 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)

Solution 2

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.

Solution 3

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;">&times;</span> </a> 
 <h1 style="font-size:30px;">&nbsp;&nbsp; <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;">&times;</span> </a> 
     <h1 style="font-size:30px;">&nbsp;&nbsp; <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;">&times;</span> </a> 
 <h1 style="font-size:30px;">&nbsp;&nbsp; <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;">&times;</span> 
   </a> 
             <h1 style="font-size:30px;">&nbsp;&nbsp; The Time To Reset 
  Password Has Expired.<br> &nbsp;&nbsp; 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;">&times;</span> </a> 
         <h1 style="font-size:30px;">&nbsp;&nbsp; The Hash Key DO Not Match. 
            <br/> &nbsp;&nbsp;&nbsp;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;">&times;</span> </a> 
     <h1 style="font-size:30px;">&nbsp;&nbsp; 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;">&times;</span> </a> 
         <h1 style="font-size:30px;">&nbsp;&nbsp; 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;">&times;</span> </a> 
         <h1 style="font-size:30px;">&nbsp;&nbsp; The Password Is 
            Successfully Reset.<br>&nbsp;&nbsp; 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?

Solution 4

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

Solution 5

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.

Share:
52,402
vigamage
Author by

vigamage

Updated on July 05, 2022

Comments

  • vigamage
    vigamage almost 2 years

    I am currently implementing a forgot password function in a Java project. my methodology is,

    1. User clicks the forgot password link.
    2. In the forgot password page, system prompts the user to enter the email address he/she has registered in to the system.
    3. An email which contains a link to reset the password is sent to the given email address in step above.
    4. User clicks the link and he/she get redirected to a page(reset password) where user can enter his new password.
    5. In Reset Password page, the field "email address" is filled automatically and it cannot be changed.
    6. Then user enter his new password and the field related to the email address in the database is updated.

    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?