it-swarm.com.de

Implementierung der Funktion für vergessene Passwörter in Java

Ich implementiere derzeit eine Passwort-Funktion in einem Java-Projekt. meine methodologie ist,

  1. Der Benutzer klickt auf den Link "Passwort vergessen".
  2. Auf der Seite "Passwort vergessen" fordert das System den Benutzer auf, die E-Mail-Adresse einzugeben, in der er sich im System registriert hat.
  3. Eine E-Mail mit einem Link zum Zurücksetzen des Kennworts wird an die angegebene E-Mail-Adresse im obigen Schritt gesendet.
  4. Der Benutzer klickt auf den Link und wird zu einer Seite weitergeleitet (Passwort zurücksetzen), auf der der Benutzer sein neues Passwort eingeben kann.
  5. Auf der Seite Passwort zurücksetzen wird das Feld "E-Mail-Adresse" automatisch ausgefüllt und kann nicht geändert werden.
  6. Dann gibt der Benutzer sein neues Passwort ein und das Feld für die E-Mail-Adresse in der Datenbank wird aktualisiert.

Obwohl ich das Feld email address auf der Seite zum Zurücksetzen des Passworts auf die Bearbeitung beschränkt habe (ein schreibgeschütztes Feld), kann jeder die URL in der Adressleiste des Browsers und das E-Mail-Adressfeld ändern.

Wie kann ich jeden Benutzer daran hindern, die E-Mail-Adresse auf der Seite zum Zurücksetzen des Kennworts zu ändern?

16
vigamage

Sie müssen es in DB speichern, bevor Sie eine E-Mail mit dem Token senden:

  1. Wenn der Benutzer auf "E-Mail mit Anweisungen zum Zurücksetzen senden" klickt, erstellen Sie einen Datensatz in der Datenbank mit den folgenden Feldern: email, token, expirationdate
  2. Benutzer erhalten eine E-Mail mit yourwwebsite.com/token und klicken Sie darauf
  3. Mit der Variable token in der URL kann der Server identify the user überprüfen, ob die Anforderung aufgrund des Ablaufdatums nicht abgelaufen ist. Geben Sie die richtige E-Mail-Adresse in die Box ein und fragen Sie nach der Kennworterneuerung. Der Benutzer gibt neue Kennwörter ein, und Sie müssen dem Server das Token (hidden field im Formular) + Kennwörter übergeben. Der Server interessiert sich nicht für das Textfeld für die E-Mail, da with the token, user is identified strongly
  4. Dann prüft der Server, ob das Token mit expirationdate noch einmal gültig ist, und überprüft, ob password match und wenn alles in Ordnung ist, neues Passwort speichern! Der Server kann erneut eine Nachricht senden, um den Benutzer darüber zu informieren, dass das Kennwort aufgrund der Anforderung geändert wurde.

Das ist wirklich sicher. Verwenden Sie kurze Zeit für expirationdate, um die Sicherheit zu verbessern (zum Beispiel 5 Minuten sind für mich richtig) und verwenden Sie ein starkes Token (als GUID, siehe Kommentare).

35
clement

Ich stimme der Antwort von @clement zu, wenn Sie die Funktion "Passwort vergessen" selbst implementieren MÜSSEN. Klingt nach einem vernünftigen und sicheren Weg für diese Implementierung.

Wenn Sie es jedoch nicht selbst implementieren müssen, würde ich vorschlagen, einen Dienst zu verwenden, der dies für Sie tut, wie Stormpath .

Wenn Sie sich für Stormpath entscheiden, würde der Code, der die Funktionalität auslösen würde, in Java (mit Stormpaths Java SDK) folgendermaßen aussehen:

Account account = application.sendPasswordResetEmail("[email protected]");

Ihr Benutzer erhält eine E-Mail mit einem Link wie:

http://yoursite.com/path/to/reset/page?sptoken=$TOKEN

Wenn der Benutzer dann auf den Link klickt, wird das Kennwort folgendermaßen überprüft und zurückgesetzt:

Account account = application.resetPassword("$TOKEN", "newPassword");

Einzelheiten dazu, wie dies funktioniert, finden Sie in der password reset-Dokumentation von Stormpath .

Bei diesem Ansatz müssen Sie die Funktionalität nicht selbst implementieren und verwalten, wenn Sie die Möglichkeit haben, dies nicht zu tun.

Hinweis: Stormpath ist Okta beigetreten.

5
ecrisostomo

Sie können die E-Mail-Adresse nicht auf die Änderung durch den Benutzer beschränken.
E-Mail-Adressen können leicht geändert werden, indem Sie den Quellcode im Browser bearbeiten, auch wenn Sie ein ausgeblendetes Textfeld als schreibgeschützt angesehen haben. 

Sie können uniq random string or token mit einem Reset-Link und Die E-Mail-Adresse und die Token-Kombination nach dem Klicken auf den Link zum Zurücksetzen des Passworts oder nach der vom Benutzer übermittelten Anforderung zum Zurücksetzen des Passworts angeben, indem Sie die E-Mail-Adresse und die Tokenzeichenfolge in der Anfrage mit der E-Mail-Adresse und der Tokenzeichenfolge überprüfen Ihre Datenbank 

Wenn die E-Mail-Adresse in Ihrer Datenbank vorhanden ist, bedeutet dies, dass die E-Mail-Adresse gültig ist. Wenn nicht, geben Sie die Nachricht ein, dass die E-Mail-Adresse in Ihren Benutzerdatensätzen nicht vorhanden ist.

HINWEIS :
Wenn Sie ein Framework oder ein einfaches Servlet verwenden, ist es besser, einen Link bereitzustellen, sodass Sie die E-Mail- und Tokenzeichenfolge überprüfen können, bevor Sie das Formular zum Zurücksetzen des Kennworts anzeigen. Wenn die Tokenzeichenfolge oder E-Mail-Adresse ungültig ist, können Sie den Benutzer daran hindern, die Anforderung zum Zurücksetzen des Kennworts zu senden und nach dem Senden der Anforderung zu bestätigen. Es ist sicherer als die Validierung nach dem Absenden der Anforderung zum Zurücksetzen des Passworts.

3
Yagnesh Agola

Diese Frage wurde 3 Jahre vor dieser Antwort gepostet ... Dennoch denke ich, dass sie für andere hilfreich sein könnte.

Kurz gesagt: Ich stimme vollkommen mit Ihrem Fluss überein. Sieht sehr sicher aus und Ihr einziges offenes Ende macht auch Sinn - wie können Sie sicherstellen, dass niemand den Benutzernamen ändert und dadurch ein neues Kennwort für ihn festgelegt wird.

Ich mag weniger die Idee, Dinge vorübergehend zu speichern, ist die DB (wie die akzeptierte Antwort darauf hindeutet).

Die Idee, über die ich nachgedacht habe, war die Signatur der Daten in dem Link, die an den Benutzer gesendet werden. Wenn der Benutzer auf den Link klickt und der Server den Anruf erhält, erhält der Server auch den verschlüsselten Teil und kann diesen bestätigen Die Daten wurden nicht berührt.

Übrigens (hier kommt eine "Promotion"): Ich habe ein Java-Projekt für diese Anwendungsfälle implementiert (auch "Konto erstellen", "Passwort ändern" usw.). Es ist kostenlos auf GitHub, Open Source. Es beantwortet Ihre Frage perfekt ... in Java implementiert, zusätzlich zu Spring Security.

Es gibt Erklärungen für alles (und wenn etwas fehlt - lass es mich wissen ...)

Schauen Sie doch mal nach: https://github.com/OhadR/oAuth2-sample/tree/master/authentication-flows

Sehen Sie eine Demo hier .

Es gibt auch eine Client-Web-App, die die Auth-Flows verwendet, mit dem README mit allen Erklärungen: https://github.com/OhadR/Authentication-Flows

3
OhadR

Es gibt zwei gängige Lösungen:

1. Creating a new password on the server and inform user from it.
2. Sending a unique URL to reset password.

Die erste Lösung hat viele Probleme und ist nicht angemessen zu verwenden. Dies sind einige Gründe:

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.

Die zweite Lösung ist also besser zu verwenden. Sie sollten jedoch die folgenden Probleme berücksichtigen:

- 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.

Die gängige Lösung kann das Generieren einer URL sein, um ein eindeutiges Token zu erstellen, das als URL-Parameter gesendet werden kann. Es enthält eine URL, z.

2
MMKarami

Wenn Sie nach dem vollständigen Code suchen, um ein vergessenes Passwort zu implementieren, gebe ich hier meinen Code frei .. __ Setzen Sie den Link an die Stelle, an der Sie brauchen.

<button> <a href="forgotpassword.jsp" style="text-decoration:none;">Forgot 
Password</a></button>

Unten ist meine forgotpassword.jsp Seite.

 <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>

Sobald die E-Mail gesendet wurde, wird sie zur mymail_fp.jsp-Seite weitergeleitet, auf der ich die E-Mail an den Benutzer sende Im Folgenden finden Sie die mymail.jsp-Seite.

<% 
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 protected]" // 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>
  <%    
  }
  %> 

Nun, was ich hier getan habe, ist, bevor ich eine E-Mail an den Benutzer schicke, speichere ich gesendete Zeit, verfahre Zeit, generiere eine Zufallszahl von 0 bis 1000000 und verkette mit der gesendeten Zeit und verschlüssele es und sende es als Abfragezeichenfolge im Link die Email. Es wird also eine E-Mail gesendet und ein Link zum Kennwort zusammen mit dem Hash-Schlüssel gesendet. Wenn der Benutzer nun auf den Link klickt, wird er an reset_password.jsp gesendet und es folgt die Seite reset_password.jsp.

<%
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();
  }
%> 

Auf dieser Seite rufe ich den Hash-Schlüssel ab und vergleiche ihn mit dem Datenbank-Hash-Schlüssel, und es ist wahr, dann hole ich die abgelaufene Zeit ab und vergleiche sie mit der aktuellen Zeit. Wenn die Zeit zum Zurücksetzen des Passworts nicht abgelaufen ist, zeige ich das Formular zum Zurücksetzen des Passworts. Andernfalls werfe ich eine Fehlermeldung aus. Wenn die Zeit noch nicht abgelaufen ist, zeige ich ein Formular und wenn das Formular gesendet wird, wird es zu update_reset.jsp umgeleitet, und im Folgenden wird meine Seite update_reset.jsp angezeigt.

 <%  
 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();
       }    
  } 
%>

Auf dieser Seite validiere ich zuerst die Felder und dann aktualisiere ich die Datenbank mit dem neuen Passwort. Es ist zwar lang, aber es funktioniert. Ich habe hier die MD5-Verschlüsselungstechnik verwendet und wenn Sie möchten, wie Sie das tun sollen, folgen Sie dem Link Wie kann ich MD5-Hash zur Sicherung von Login-Passwörtern in JSP mit Javascript verwenden?

1
Bhagawat